At the end of the day, all these approaches achieve the same goal - run some user-defined actions upon the instance initialization.
Launch Configuration and Launch Template allow you to specify the configuration of your instance once and then reuse it in multiple places. With or without CloudFormation. Launch Configuration is specific to the AutoScaling group. If you need to spin up an instance, that is not in the autoscaling group, use Launch Template to achieve the same result.
Now, in both cases above you can use either Bash script in UserData or AWS::CloudFormation::Init.
Bash in UserData is just that - Bash script. If you are familiar with it and feel confident that you can achieve what you need in just Bash - go for it. AWS::CloudFormation::Init is a higher-level abstract, simplifying bunch of things, such as file creation, permissions etc. Nothing you can't do with just bash, but surely making it easier and more maintainable.
One thing to keep in mind - bash+userdata approach would work as is on all cloud providers, not limited to AWS. Google, Azure. -they would let you run the same scripts with maybe minor modifications. AWS::CloudFormation::Init is AWS - specific.