This post originally appeared on Engine Yard
Where Did It Come From?
But perhaps the most unique feature of Rollup is the fact that it only requires modules that you’ve actually imported somewhere. This means that unused code never makes it into the bundle. Note that this is slightly different than Dead Code Elimination. Roman Luitikov does a good job explaining this distinction in his post about Tree Shaking.
At this moment, a lot of people in the JS community are coalescing around Webpack as a build tool. Webpack’s maintainers are currently working on a Webpack 2 release. One of its interesting features is that it will support native ES6 modules without first transforming them into a CommonJS format. This is good, because tree shaking won’t work with CommonJS modules.
Recently, the estimable Dr. Axel Rauschmayer wrote a popular post about setting up tree-shaking with Webpack 2. I decided to give it a try in my colleague Marc Garreau’s Redux Starter Kit, to see what it would actually take to get tree shaking to work in a full project. You can see the result of my experiments on my GitHub.
How Does It Work?
The steps involved in tree shaking are fairly simple.
You write your ES6 code as normal, importing and exporting modules as needed. When it comes time to create a bundle, Webpack grabs all of your modules and puts them into a single file, but removes the
export from code that’s not being imported anywhere. Next, you run a minification process, resulting in a bundle that excludes any dead code found along the way. If you’re curious, check Dr. Rauschmayer’s post for more details.
Setting It Up
Since Webpack 2 is still in beta, you’ll need to update your
package.json to point at the beta version. But before we do that let’s also talk about our Babel preset. Typically, I use the
es2015 preset, but this preset relies on the
transform-es2015-modules-commonjs plugin, which won’t work for tree shaking. Dr. Rauschmayer pointed this out in his post. At the time of his writing, the best workaround was to copypasta all of the plugins in that preset except
Thankfully, we can now get around this copy-pasta’ing by including the
es2015-webpack preset instead. Both of these presets support native ES6 modules.
Let’s install Webpack 2 and the
es2015-native-modules Babel preset by running
npm install --save babel-preset-es2015-native-modules firstname.lastname@example.org.
You should see these packages appear in your
package.json dependencies section:
1 2 3 4 5
You’ll also need to update your
.babelrc file to use the new preset:
1 2 3
Remember that we need to run a minification step during our bundle in order to take advantage of dead code elemination. Change the build script to use the
--optimize-minimize flag on our call to the Webpack executable:
1 2 3 4 5 6 7
Finally, update your Webpack config to make sure we’re only listing one loader for at a time. This is for compatibility with Webpack 2, since it expects a slightly different syntax in the config.
1 2 3 4 5 6 7 8 9 10
Taking It For A Spin
Now we have everything we need in place to run a build with tree shaking. Let’s try it out!
To make sure everything is working, we’ll need to export some modules that we’re not importing. In my example, I decided to create some useless functions.
1 2 3 4 5 6 7 8 9
Then in my actual project code, I only imported and used one of them.
1 2 3 4
When we run
npm run build, we should end up with a minified build that excludes the
makeMeASandwich code. When you run this command, you’ll see an output that accounts for removed modules. In my example, I saw a bunch of warnings from dependencies such as React and Webpack Hot Middleware. I left a few of them in the pasted output below, but the line that’s of most interest of us is
Dropping unused function makeMeASandwich, since that’s the code we’re checking on.
1 2 3 4 5 6 7 8
Now if we open up the minified bundle and search for the contents of the
makeMeASandwich function, we won’t be able to find it. Indeed, searching for
make sandwich: operation not permitted yields no results. It works for
one open faced club sandwich coming right up, though. Success!
Going Further: Shaking Dependencies
Removing your own unused code is all well and good, but it’s probably only going to be a drop in the bucket of your overall bundle. The place where tree shaking really shines is in helping remove usused code from dependencies. If you’re pulling in a whole package, but only using a single export from it, why would you send the rest of the package to your users?
Roman Luitikov’s post did a great job of walking through what it looks like to tree shake a dependency by showing what happens if you pull in Lodash, but only import and use the
first function. The rest of Lodash gets thrown out, and the resulting build is much smaller!
Since Roman has already covered this, I won’t go into the details of what that looks like here, but you can see it in my example if you’re curious.
The main point to understand is that when you consider the sizeable amount of unused code that ends up in a typical JS project when you run
npm install, removing it with tree shaking can yield huge savings on bundle size.
Tree shaking offers a great way to cut down on your bundle size, and it’s easy to get set up in your existing project. I’m excited to see how this practice evolves as more of the community begins to embrace it and Webpack 2 is released. In the meantime, I would recommend reading up on some other examples of tree shaking and more of the features coming in Webpack 2.
Until next time, happy coding!
- If you decide to give tree shaking a try in your existing project, we’d love to hear how it goes. Leave us a comment!