1. 16
    Tree shaking with Webpack 2
    3m 23s
⚠️ This lesson is retired and might contain outdated information.

Tree shaking with Webpack 2

Kent C. Dodds
InstructorKent C. Dodds
Share this video with your friends

Social Share Links

Send Tweet
Published 8 years ago
Updated 2 years ago

The less code you can send to the browser, the better. The concept of tree shaking basically says that if you’re not using some piece of code, then exclude it from the final bundle, even when that piece of code is exported from a module. Because ES6 modules are statically analyzable, Webpack can determine which of your dependencies are used and which are not. In this lesson, see how to take advantage of this awesome feature in Webpack 2.

[00:00] One of the aspects of ES6 modules is that they're statically analyzable. This means tools like Webpack can predict exactly which exports are used and which are not. Webpack takes advantage of this with a feature called "treeshaking." This feature can seriously reduce bundle sizes in large applications.

[00:15] Let's take a look at our helpers file here, we have this function addClass down here below. The only place the function is being used is in the app.js file, meaning that if we removed it from the app.js file, it's no longer needed in our bundle.

[00:28] Let's go ahead and do that, and now, we can open up our app, everything is still working. But if we look in thehelpers.js file, we see the addClass function is still being exported.

[00:39] That's to be expected, because we're still exporting this from the helpers file, and Bable is transpiling our ES6 module exports into common JS exports. Because this is getting transpiled down to common JS, it's not statically analyzable like ES6 modules, and so, Webpack can't reliably treeshake this function, and our bundle will include it even though it's not in use.

[01:02] We're going to use a different Bable preset, which excludes the transpilation of ES6 modules and leave that to Webpack.

[01:08] Now, let's install Bable Preset ES2015 Webpack, and now, if we open up our bable.rc file, we'll trade out the ES2015 with ES2015 Webpack preset, and then, just for the sake of completeness, we'll go ahead and get into our package.JSON and remove the original preset, because the Webpack version of this preset will have all of the other transforms that we need. We'll save that, and now, we'll run npm start to start up our application.

[01:38] This will automatically refresh, and now, we'll see that the helpers file is a little bit different with regards to how these ES6 module exports are transpiled. Before it was pretty straightforward common JS. Now, it's a little bit different because Webpack is going the transpilation for us.

[01:55] Because Webpack has an understanding of all the modules throughout our system, it realizes addClass is not being used anywhere in our application, and so, it adds this comment, "Unused harmony export addClass," and it excludes it from the list of things that are being added to our common JS export.

[02:13] You'll notice though, that the addClass function is still being added in here, but because it's not being referenced, when we go ahead and uglify our code using the uglify.js plug-in, this function will be automatically removed.

[02:25] We can verify that by running our production build. To do this, we can run NPM run build prod, which as you can see, is running node emv production Webpack with the -e flag, and if we scroll up here, we'll see this warning from uglify.js, "Dropping unused function addClass."

[02:42] Because that addClass function is not being referenced in any of the code inside of the helpers file, it's being dropped from our bundle, and we're not shipping that unused function to browsers.

[02:52] This applies to more than just our application code, this would apply to any ES6 module that we're importing into our application, including third-party dependencies.

[03:01] In review, all we had to do to leverage treeshaking with Webpack 2, is to install Bable preset ES2015 Webpack so that Bable doesn't transpile our ES6 modules, and then, we update our Bable.rc file to use that preset instead of the Bable preset ES2015 module. That's how you leverage treeshaking with Webpack 2.

Peng SONG
Peng SONG
~ 8 years ago

I don't understand why it does not remove unused code in my project...

My webpack version is 2.1.0-beta.13.

I define the js like below:

export {a, b, c, d}

function a(){}
function b(){}

And I make sure that function b is never used. But after webpack build (with uglify plugin enabled), it turns out to be:

function(module,exports,__webpack_require__){"use strict";eval('\n/* harmony export */ __webpack_require__.d(exports, "a", function() { return a; });/* unused harmony export b */\n\nfunction a() {}\nfunction b() {}//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMjAwLmpzIiwic291cmNlcyI6WyJ3ZWJwYWNrOi8vL3NyYy9wYWdlX2luZGV4L3BhcnRpYWwuanM/MjE5YiJdLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQge2EsIGJ9O1xuXG5mdW5jdGlvbiBhICgpIHtcbn1cbmZ1bmN0aW9uIGIgKCkge1xufVxuXG5cblxuLy8gV0VCUEFDSyBGT09URVIgLy9cbi8vIHNyYy9wYWdlX2luZGV4L3BhcnRpYWwuanMiXSwibWFwcGluZ3MiOiJBQUFBO0FBQ0E7QUFDQTtBQUVBOyIsInNvdXJjZVJvb3QiOiIifQ==')}

You see, function b is still included...

Here is my uglify config:

  new webpack.optimize.UglifyJsPlugin({
    compress: {
      dead_code: true,
      unused: true,
    }
  }),
Kent C. Dodds
Kent C. Doddsinstructor
~ 8 years ago

Hi Peng, Are you putting that as your entry file? If so then I don't believe that tree shaking will work because webpack cannot be sure what of your entry file is used and what is not (you could be distributing a library for example which will have external users). That's the only thing I can think of. Other than that, I'm not sure what's going wrong with your case. Sorry!

Paul Kamma
Paul Kamma
~ 8 years ago

When I try to make my Production Build with the es2015-webpack preset I get the following Error:

ERROR in bundle.js from UglifyJs
Unexpected token: name (React) [./index.jsx:1,7]

That's how my .babelrc looks like:

{
	"presets" : ["es2015-webpack", "react", "stage-1"],
	"plugins" : ["transform-decorators-legacy","transform-class-properties","transform-object-rest-spread","transform-object-assign"]
}

Did I miss something?

Kent C. Dodds
Kent C. Doddsinstructor
~ 8 years ago

Hmmm... It looks ok. I would recommend that to debug the problem you comment out the UglifyJs plugin and see what the bundle looks like. Does it run? Make sure you're using the latest webpack 2. It's still in beta, so you have to install it explicitly.

Paul Kamma
Paul Kamma
~ 8 years ago

Ah ok. I think thats the problem, I use webpack 1.12.15 :) Thanks.

Mike thompson
Mike thompson
~ 8 years ago

What would be different if you had a Typescript project instead of a Babel project?

Kent C. Dodds
Kent C. Doddsinstructor
~ 8 years ago

I've gotten this question a lot :-) See: https://github.com/kentcdodds/ama/issues/154

Riva-Dev
Riva-Dev
~ 8 years ago

the course doesnt starts from scratch, and that made me confused...

Kent C. Dodds
Kent C. Doddsinstructor
~ 8 years ago

This course assumes some pre-existing webpack knowledge. If you're entirely new to webpack, you can watch some of the more beginner lessons in this playlist.

CTdev
CTdev
~ 8 years ago

The babel-preset-es2015-webpack preset is not needed anymore since the latest babel release allows you to specify preset options. https://github.com/gajus/babel-preset-es2015-webpack/issues/14#issuecomment-237869435

Lingke
Lingke
~ 8 years ago

Hi there - the video refers to the use of UglifyJS in webpack. I don't see where in webpack.config.js where the UglifyJS plugin is used. Am I missing something?

Also - for newcomers, check out the WIP docs for webpack 2.0: https://gist.github.com/sokra/27b24881210b56bbaff7 - it explains how to use (env) => { ... } as a webpack config versus a straight up object as in 1.x

Negin Basiri
Negin Basiri
~ 8 years ago

Seems we need to set the 'module': false for preset es2015. I'm using babel loader and here is my module config module: { loaders: [ { test: /.js$/, exclude: /node_modules/, loader: 'babel', query: { presets: ['es2015', 'react'] } }, { test: /.scss$/, loader: ExtractTextPlugin.extract("style-loader", "css-loader!sass-loader") } ] },

I tried presets: [['es2015', {modules: false}], 'react'] but I got error on import line for my React component.

How should I set module to false?

Kent C. Dodds
Kent C. Doddsinstructor
~ 8 years ago

Give it another shot, I made some updates.

Negin Basiri
Negin Basiri
~ 8 years ago

Thanks for reply Kent. Did you make some change to the video or a change to the webpack. which one should work

presets: [['es2015', {modules: false}], 'react'] or presets: ['es2015', 'react', {modules: false}]

?

Thanks

Kent C. Dodds
Kent C. Doddsinstructor
~ 8 years ago

The video hasn't been updated. The correct configuration is:

presets: [['es2015', {modules: false}], 'react']
apollo
apollo
~ 7 years ago

i check out your code from github: https://github.com/kentcdodds/es6-todomvc/tree/lesson/treeshaking and run npm start, but it gives me the following error:

webpack result is served from /
content is served from /Users/apollotang/_a/m/me/egghead/Using-Webpack-for-Production-JavaScript-Applications/codes/fork/es6-todomvc-copy2/dist
/Users/apollotang/_a/m/me/egghead/Using-Webpack-for-Production-JavaScript-Applications/codes/fork/es6-todomvc-copy2/node_modules/loader-runner/lib/loadLoader.js:35
			throw new Error("Module '" + loader.path + "' is not a loader (must have normal or pitch function)");
			^

Error: Module '/Users/apollotang/_a/m/me/egghead/Using-Webpack-for-Production-JavaScript-Applications/codes/fork/es6-todomvc-copy2/node_modules/eslint/lib/api.js' is not a loader (must have normal or pitch function)
Kent C. Dodds
Kent C. Doddsinstructor
~ 7 years ago

Hmmm... I'm not sure why you would be getting that error. If I do a fresh clone/install/start, everything works as expected. Here are the commands I ran:

git clone https://github.com/kentcdodds/es6-todomvc.git
cd es6-todomvc
git checkout lesson/treeshaking
npm install
npm start

With that, things run and I see the app come up.

Maxime
Maxime
~ 7 years ago

I think we don't need to do this anymore with Webpack2 ? https://github.com/gajus/babel-preset-es2015-webpack

"This preset is used to enable ES2015 code compilation down to ES5. webpack 2 natively supports ES6 import and export statements. webpack 2 leverages the static structure of the ES6 modules to perform tree shaking."

Kent C. Dodds
Kent C. Doddsinstructor
~ 7 years ago

The video hasn't been updated. The correct configuration is:

presets: [['es2015', {modules: false}], 'react']

So yes, you do not need the es2015-webpack plugin, just use the normal es2015 plugin with the new configuration and you should be good.

Markdown supported.
Become a member to join the discussionEnroll Today