Skip to content

Commit cdae9d0

Browse files
committed
update tree shaking guide for webpack 4
1 parent 2c75064 commit cdae9d0

File tree

1 file changed

+34
-46
lines changed

1 file changed

+34
-46
lines changed

src/content/guides/tree-shaking.md

Lines changed: 34 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ contributors:
88
- avant1
99
- MijaelWatts
1010
- dmitriid
11+
- probablyup
1112
related:
1213
- title: Tree shaking with webpack 2, TypeScript and Babel
1314
url: https://alexjoverm.github.io/2017/03/06/Tree-shaking-with-Webpack-2-TypeScript-and-Babel/
@@ -23,7 +24,7 @@ related:
2324

2425
_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).
2526

26-
The webpack 2 release came with built-in support for ES2015 modules (alias _harmony modules_) as well as unused module export detection.
27+
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.
2728

2829
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.
2930

@@ -107,23 +108,46 @@ function cube(x) {
107108
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.
108109
109110
110-
## Minify the Output
111+
## Mark the file as side-effect-free
112+
113+
In a 100% ESM module world, identifying side effects is straightforward. However, we don't live in that reality just yet, so it's necessary to provide hints to webpack's compiler on the "pureness" of your code.
114+
115+
The way this is accomplished is the `"sideEffects"` package.json property.
116+
117+
```json
118+
{
119+
"name": "your-project",
120+
"sideEffects": false
121+
}
122+
```
111123
112-
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...
124+
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.
113125
114-
Let's start by installing it:
126+
T> A "side effect" is defined as code that performs a special behavior when imported, other than simply exposing one or more exports. For instance: a polyfill which adjusts the global scope but does not provide an export or has one that you are not using in your codebase.
115127
116-
``` bash
117-
npm install --save-dev uglifyjs-webpack-plugin
128+
If your code did have some side effects though, an array can be provided instead:
129+
130+
```json
131+
{
132+
"name": "your-project",
133+
"sideEffects": [
134+
"./src/some-side-effectful-file.js"
135+
]
136+
}
118137
```
119138
120-
And then adding it into our config:
139+
## Minify the Output
140+
141+
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.
142+
143+
T> Note that the `--optimize-minimize` flag can be used to insert the `UglifyJsPlugin` as well.
144+
145+
As of webpack 4, this is also easily toggled via the `"mode"` config option, set to `"production"`.
121146
122147
__webpack.config.js__
123148
124149
``` diff
125150
const path = require('path');
126-
+ const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
127151

128152
module.exports = {
129153
entry: './src/index.js',
@@ -132,57 +156,21 @@ module.exports = {
132156
path: path.resolve(__dirname, 'dist')
133157
- }
134158
+ },
135-
+ plugins: [
136-
+ new UglifyJSPlugin()
137-
+ ]
159+
+ mode: "production"
138160
};
139161
```
140162
141-
T> Note that the `--optimize-minimize` flag can be used to insert the `UglifyJsPlugin` as well.
142-
143163
With that squared away, we can run another `npm run build` and see if anything has changed.
144164
145165
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.
146166
147167
148-
## Caveats
149-
150-
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:
151-
152-
__transforms.js__
153-
154-
``` js
155-
import * as mylib from 'mylib';
156-
157-
export const someVar = mylib.transform({
158-
// ...
159-
});
160-
161-
export const someOtherVar = mylib.transform({
162-
// ...
163-
});
164-
```
165-
166-
__index.js__
167-
168-
``` js
169-
import { someVar } from './transforms.js';
170-
171-
// Use `someVar`...
172-
```
173-
174-
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.
175-
176-
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.
177-
178-
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.
179-
180-
181168
## Conclusion
182169
183170
So, what we've learned is that in order to take advantage of _tree shaking_, you must...
184171
185172
- Use ES2015 module syntax (i.e. `import` and `export`).
173+
- Add a "sideEffects" entry to your project's `package.json` file.
186174
- Include a minifier that supports dead code removal (e.g. the `UglifyJSPlugin`).
187175
188176
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.

0 commit comments

Comments
 (0)