Imagine if there was a build tool that you could use for Vue.js projects that compiled faster than Webpack, gave you a smaller bundle size, and required only a few lines of configuration.
Brunch is that tool. In this article, I’ll show you how incredibly easy it is to set up a Vue.js + Brunch project, cover some of the pros and cons of Brunch, and serve up a few tasty brunch puns on the way.
To whet your appetite, take a look at this side-by-side comparison of a Webpack config and a Brunch config for the same, simple Vue.js project:
Webpack is a crucial tool for building sophisticated, highly optimized web applications, and is encouraged by many influential web developers.
However, for newer developers, or those working on less ambitious projects, Webpack mostly occurs as a barrier. For Vue.js projects, users feel they must choose either to develop in a lo-fi ES5 environment without the cool features like single-file components or to develop in a highly sophisticated environment with as much time spent on build config as on app development.
Make Room for Brunch
Brunch has been around since 2012, and while it is the default build tool for the Phoenix Elixir framework, it is still surprisingly obscure. That said, it has a dedicated group of users who love it for its core philosophy of “simplicity and speed,” something that Vue.js users may identify with.
Despite its obscurity, there are almost 80 community made plugins for most imaginable automations like transpiling, linting, optimizing, etc. Brunch can do most of the things you’ll need in a Vue project too: wrap files as modules, concatenate them into a build file, compile Vue files, transpile JS and SASS, and so on.
To be clear, Brunch is not as full-featured as Webpack and has certain limitations. For example, Brunch does not yet support dynamic imports, and it doesn’t process images and fonts.
You may also like:
A Dirty Webpack Trick That Reduced Our gzipped Bundle Size by 55KB.
Convention Over Configuration
A defining feature of Brunch is that it is opinionated and favors convention over configuration. If you’re willing to structure your project the “Brunch way” and you’re happy with the standard settings of most plugins, you may need only a handful of lines of configuration for a surprisingly sophisticated build.
Take the example of precompiling SASS. With Webpack, each project must declare loaders for the file type that will be processed. A typical SASS configuration would be this:
With Brunch, however, all you need to do is install the Brunch SASS plugin. Brunch will scan your package.json when a build is triggered, and, seeing that you’ve installed the plugin, will take care of it entirely.
Brunch Taste Test
To see what Brunch can do, I’ve installed a Vue.js project using the Vue CLI webpack-simple project template. After building the boilerplate code with Webpack I get this:
I’ll now migrate this project to Brunch and attempt to recreate the same build features and processes as Webpack, so as to highlight any similarities and differences.
Feel free to download the completed code from this repo and follow along.
Like Webpack, it’s best to install Brunch globally so the Brunch CLI can be run from anywhere.
And also install it locally:
Unlike Webpack, Brunch does not require an entry file. You instead have a watch directory, and Brunch will simply process every file there if it can.
Despite the lost pun opportunity, the default watch directory in Brunch is app, not src. Using app in this project will take advantage of Brunch’s minimal configuration, so I’ll move all the project files there:
With that done, I can run the first build:
That results in this pleasantly brief output message:
And a new public directory is created containing the following build files:
Brunch has wrapped main.js as a CommonJS module. It hasn’t imported Vue or App.vue, though, and has not transpiled down to ES5. We’ll need some additional plugins for those tasks.
Another convention of Brunch is that any directory called assets will be recursively copied to the public folder without any processing, which is why you see logo.png in the output.
Brunch does not load image or font files like Webpack does, so copying to the output folder is probably the best option.
To process the project files, I’ll need to add some plugins to Brunch. There’s ES6 code as well as the Vue file, which include SASS, so I’ll install the appropriate plugins for those file types:
Amazingly, that’s all that’s required. When I build again I get this output:
Checking the build file public/app.js again, there’s a whole lot more code. This is because Brunch transpiled the ES6 in main.js, found the dependency of Vue and added that, and has also processed and imported App.vue.
How can Brunch do this without any configuration? It sees these plugins in the dependencies in package.json and simply registers them with default settings.
I’ve built all the project code now, so it’s time to go to the browser and see what I’ve got.
Like Webpack, Brunch has a built-in development server that I can use to serve the project. It will also watch any files for changes and automatically build the changes (very fast too, I might add).
Before I run the server, though, I’ll move index.html to the assets directory so it gets copied into the public folder and can be served as well:
I can now run the server:
And I see this terminal output:
When I check the browser, though, all I get is a blank screen. The issue is that since there’s no entry file specified, the project won’t immediately run like you’d expect with Webpack. The entry file must be manually called.
Remember that Brunch wraps all files into CommonJS modules in the output like this:
The convention is that the module is named by its file name minus the extension, so main.js, is just main. I need to now call that module in index.html after the script has been downloaded:
Okay, almost there. Refreshing the browser again, I get this error:
This is because the Brunch plugin API is not quite as powerful as Webpack, and to be able to inline the CSS at runtime, vue-brunch requires the vueify-insert-css module to be available.
This will need to be imported at the top of main.js:
With that done, I’ve got myself a functioning Vue app again:
Before I can compare the Brunch approach to Webpack, though, I also need to set some production optimizations to ensure both approaches are producing an equivalent output.
Webpack has a configuration option to turn off the annoying development mode message automatically. As far as I know, that can’t be done with Brunch, so I’ll have to add this line to main.js after importing Vue:
brunch-uglify-js for this:
As you might guess, no further configuration is required. All I need to do is add the
-p (production) switch to the Brunch command when I build, and the output will be uglified.
Easy as pie!
I’ve now successfully replaced the build functionality of webpack-simple with Brunch. Let’s now compare the difference in configuration files.
(Note that the above has no
file-loader configuration since that can’t be accomplished in Brunch).
And now, Brunch:
As you can see, Brunch requires significantly less configuration if you follow its conventions. To be fair, with Brunch I did have to add two extra lines of code to main.js and one extra line to index.html to get equivalent functionality.
And what about size and performance? Comparing a production build from both tools:
Amazingly, Brunch has a smaller bundle size and compiles more than 3 times faster than Webpack.
I think Brunch is a great choice for simple Vue projects. It’s not only easier to set up, but also faster, and for this use case, provides a smaller bundle file.
However, this doesn’t mean Brunch is universally better than Webpack for Vue.js projects. There are many things Brunch can’t do, for example, dynamic imports which are essential for building PWAs.
The point is that while Webpack definitely has its place, Brunch should as well.
Cook Up Your Own Brunch
Just like with Vue CLI, you can create Brunch project skeletons. I recommend you first try brunch-vue-barebones which is very similar to what I’ve set up.