Skip to content

update tree shaking guide for webpack 4 #1865

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Mar 15, 2018
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 56 additions & 56 deletions src/content/guides/tree-shaking.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,19 @@ contributors:
- avant1
- MijaelWatts
- dmitriid
- probablyup
related:
- title: Tree shaking with webpack 2, TypeScript and Babel
url: https://alexjoverm.github.io/2017/03/06/Tree-shaking-with-Webpack-2-TypeScript-and-Babel/
- title: Tree-shaking with webpack 2 and Babel 6
url: http://www.2ality.com/2015/12/webpack-tree-shaking.html
- title: webpack 2 Tree Shaking Configuration
url: https://medium.com/modus-create-front-end-development/webpack-2-tree-shaking-configuration-9f1de90f3233#.15tuaw71x
- title: Issue 2867
url: https://github.com/webpack/webpack/issues/2867
- title: Issue 4784
url: https://github.com/webpack/webpack/issues/4784
- title: "webpack 4 beta — try it today!"
url: https://medium.com/webpack/webpack-4-beta-try-it-today-6b1d27d7d7e2#9a67
- title: Debugging Optimization Bailouts
url: https://webpack.js.org/plugins/module-concatenation-plugin/#debugging-optimization-bailouts
- title: Issue 6074 - Add support for more complex selectors for sideEffects
url: https://github.com/webpack/webpack/issues/6074
---

_Tree shaking_ is a term commonly used in the JavaScript context for dead-code elimination. It relies on the [static structure](http://exploringjs.com/es6/ch_modules.html#static-module-structure) of ES2015 module syntax, i.e. [`import`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import) and [`export`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export). The name and concept have been popularized by the ES2015 module bundler [rollup](https://github.com/rollup/rollup).

The webpack 2 release came with built-in support for ES2015 modules (alias _harmony modules_) as well as unused module export detection.
The webpack 2 release came with built-in support for ES2015 modules (alias _harmony modules_) as well as unused module export detection. The new webpack 4 release expands on this capability with a way to provide hints to the compiler via the `"sideEffects"` `package.json` flag to denote which files in your project are "pure" and therefore safe to prune if unused.

T> The remainder of this guide will stem from [Getting Started](/guides/getting-started). If you haven't read through that guide already, please do so now.

Expand Down Expand Up @@ -107,23 +104,62 @@ function cube(x) {
Note the `unused harmony export square` comment above. If you look at the code below it, you'll notice that `square` is not being imported, however, it is still included in the bundle. We'll fix that in the next section.
## Minify the Output
## Mark the file as side-effect-free
In a 100% ESM module world, identifying side effects is straightforward. However, we aren't there just yet, so in the mean time it's necessary to provide hints to webpack's compiler on the "pureness" of your code.
The way this is accomplished is the `"sideEffects"` package.json property.
```json
{
"name": "your-project",
"sideEffects": false
}
```
All the code noted above does not contain side effects, so we can simply mark the property as `false` to inform webpack that it can safely prune unused exports.
So we've cued up our "dead code" to be dropped by using the `import` and `export` syntax, but we still need to drop it from the bundle. To do that, we'll add a minifier that supports dead code removal -- the [`UglifyJSPlugin`](/plugins/uglifyjs-webpack-plugin) -- to our configuration...
T> A "side effect" is defined as code that performs a special behavior when imported, other than exposing one or more exports. An example of this are polyfills, which affect the global scope and usually do not provide an export.
Let's start by installing it:
If your code did have some side effects though, an array can be provided instead:
``` bash
npm install --save-dev uglifyjs-webpack-plugin
```json
{
"name": "your-project",
"sideEffects": [
"./src/some-side-effectful-file.js"
]
}
```
And then adding it into our config:
The array accepts relative, absolute, and glob patterns to the relevant files. It uses [micromatch](https://github.com/micromatch/micromatch#matching-features) under the hood.
T> Note that any imported file is subject to tree shaking. This means if you use something like `css-loader` in your project and import a CSS file, it needs to be added to the side effect list so it will not be unintentionally dropped in production mode:
```json
{
"name": "your-project",
"sideEffects": [
"./src/some-side-effectful-file.js",
"*.css"
]
}
```
Finally, `"sideEffects"` can also be set from the [`module.rules` config option](https://github.com/webpack/webpack/issues/6065#issuecomment-351060570).
## Minify the Output
So we've cued up our "dead code" to be dropped by using the `import` and `export` syntax, but we still need to drop it from the bundle. To do that, we'll use the `-p` (production) webpack compilation flag to enable the uglifyjs minification plugin.
T> Note that the `--optimize-minimize` flag can be used to insert the `UglifyJsPlugin` as well.
As of webpack 4, this is also easily toggled via the `"mode"` config option, set to `"production"`.
__webpack.config.js__
``` diff
const path = require('path');
+ const UglifyJSPlugin = require('uglifyjs-webpack-plugin');

module.exports = {
entry: './src/index.js',
Expand All @@ -132,57 +168,21 @@ module.exports = {
path: path.resolve(__dirname, 'dist')
- }
+ },
+ plugins: [
+ new UglifyJSPlugin()
+ ]
+ mode: "production"
};
```
T> Note that the `--optimize-minimize` flag can be used to insert the `UglifyJsPlugin` as well.
With that squared away, we can run another `npm run build` and see if anything has changed.
Notice anything different about `dist/bundle.js`? Clearly the whole bundle is now minified and mangled, but, if you look carefully, you won't see the `square` function included but will see a mangled version of the `cube` function (`function r(e){return e*e*e}n.a=r`). With minification and tree shaking our bundle is now a few bytes smaller! While that may not seem like much in this contrived example, tree shaking can yield a significant decrease in bundle size when working on larger applications with complex dependency trees.
## Caveats
Please note that webpack doesn't perform tree-shaking by itself. It relies on third party tools like [UglifyJS](/plugins/uglifyjs-webpack-plugin/) to perform actual dead code elimination. There are situations where tree-shaking may not be effective. For example, consider the following modules:
__transforms.js__
``` js
import * as mylib from 'mylib';

export const someVar = mylib.transform({
// ...
});

export const someOtherVar = mylib.transform({
// ...
});
```
__index.js__
``` js
import { someVar } from './transforms.js';

// Use `someVar`...
```
In the code above webpack cannot determine whether or not the call to `mylib.transform` triggers any side-effects. As a result, it errs on the safe side and leaves `someOtherVar` in the bundled code.
In general, when a tool cannot guarantee that a particular code path doesn't lead to side-effects, this code may remain in the generated bundle even if you are sure it shouldn't. Common situations include invoking a function from a third-party module that webpack and/or the minifier cannot inspect, re-exporting functions imported from third-party modules, etc.
The code used in this guide assumes you perform tree-shaking using UglifyJS plugin. However, there are other tools such as [webpack-rollup-loader](https://github.com/erikdesjardins/webpack-rollup-loader) that may produce different results depending on your setup.
## Conclusion
So, what we've learned is that in order to take advantage of _tree shaking_, you must...
- Use ES2015 module syntax (i.e. `import` and `export`).
- Add a "sideEffects" entry to your project's `package.json` file.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would be if the code is not affecting outside scope, not always so we should add it to here.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually no, you should always add a sideEffects entry or treeshaking won't work.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, but if your librari does produce side effects you shouldn't, right? I don't want to give that message to package owners.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps, but in that case set it to true, or provide an array with the specific files that include side effects as described in the guide.

It's better to explicitly have the sideEffects known to webpack than to try and infer it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is required?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe is redundant since you already define what sideEffects means. I'm OK with it. Sorry for the noise.

- Include a minifier that supports dead code removal (e.g. the `UglifyJSPlugin`).
You can imagine your application as a tree. The source code and libraries you actually use represent the green, living leaves of the tree. Dead code represents the brown, dead leaves of the tree that are consumed by autumn. In order to get rid of the dead leaves, you have to shake the tree, causing them to fall.
Expand Down