Skip to content

Commit

Permalink
docs(guides): rewrite/re-outline the tree-shaking page
Browse files Browse the repository at this point in the history
This continues our work #1258 and resolves most of the issues mentioned
in #1331. It is a WIP though, we still need to populate the `TODO` code
blocks.
  • Loading branch information
skipjack committed Aug 26, 2017
1 parent 39cc0e2 commit 3fde143
Showing 1 changed file with 94 additions and 43 deletions.
137 changes: 94 additions & 43 deletions src/content/guides/tree-shaking.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,75 +14,126 @@ related:
url: https://medium.com/modus-create-front-end-development/webpack-2-tree-shaking-configuration-9f1de90f3233#.15tuaw71x
---

_Tree shaking_ is a term commonly used in the JavaScript context for dead-code elimination, or more precisely, live-code import. It relies on ES2015 module [import](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import)/[export](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export) for the [static structure](http://exploringjs.com/es6/ch_modules.html#static-module-structure) of its module system. The name and concept have been popularized by the ES2015 module bundler [rollup](https://github.com/rollup/rollup).
_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).

webpack 2 comes with a 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.

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.

## Example

Consider a **maths.js** library file exporting two functions, `square` and `cube`:
## Add a Utility

```javascript
// This function isn't used anywhere
Let's add a new utility file to our project, `src/math.js`, that exports two functions:

__project__

``` diff
webpack-demo
|- package.json
|- webpack.config.js
|- /dist
|- bundle.js
|- index.html
|- /src
|- index.js
|- math.js
|- /node_modules
```

__src/math.js__

``` javascript
export function square(x) {
return x * x;
}

// This function gets included
export function cube(x) {
return x * x * x;
}
```

In our **main.js** we are selectively importing `cube`:
With that in place, let's update our entry script to utilize this one of these new methods:

``` diff
import _ from 'lodash';
+ import { cube } from './math.js';

```javascript
import {cube} from './maths.js';
console.log(cube(5)); // 125
function component() {
var element = document.createElement('div');

// Lodash, now imported by this script
- element.innerHTML = _.join(['Hello', 'webpack'], ' ');
+ element.innerHTML = _.join([
'Hello webpack!',
'5 cubed is equal to ' + cube(5)
], '\n\n');

return element;
}

document.body.appendChild(component());
```

Running `node_modules/.bin/webpack main.js dist.js` and inspecting `dist.js` reveals that `square` is not being exported (see the "unused harmony export square" comment):
Note that we __did not `import` the `square` method__ from the `src/math.js` module. That function is what's known as "dead code", meaning an unused `export` that should be dropped. Now let's run our npm script, `npm run build`, and inspect the output:

```javascript
/* ... webpackBootstrap ... */
/******/ ([
/* 0 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
__dist/bundle.js__

"use strict";
/* unused harmony export square */
/* harmony export (immutable) */ __webpack_exports__["a"] = cube;
// This function isn't used anywhere
function square(x) {
return x * x;
}
``` js
// TODO: Display contents with `unused harmony export square`...
```

// This function gets included
function cube(x) {
return x * x * 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 exported, however, it is still included in the bundle. We'll fix that in the next section.


## Minify the Output

/***/ }),
/* 1 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
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...

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__maths_js__ = __webpack_require__(0);
Let's start by installing it:

console.log(__webpack_require__.i(__WEBPACK_IMPORTED_MODULE_0__maths_js__["a" /* cube */])(5)); // 125
``` bash
npm i --save-dev uglifyjs-webpack-plugin
```

/***/ })
And then adding it into our config:

__webpack.config.js__

``` diff
const path = require('path');
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');

module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
- }
+ },
+ plugins: [
+ new UglifyJSPlugin()
+ ]
};
```

When running a [production build](/guides/production), `node_modules/.bin/webpack --optimize-minimize main.js dist.min.js`, only the minimized version of `cube` but not `square` remains in the build:
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:

__dist/bundle.js__

```javascript
/* ... */
function(e,t,n){"use strict";function r(e){return e*e*e}t.a=r}
/* ... */
function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(0);console.log(n.i(r.a)(5))}
``` js
// TODO: Display contents with `square removed...
```

T> Note that the `--optimize-minimize` flag enables tree shaking by including the `UglifyJsPlugin` behind the scenes. Alternatively, the `UglifyJsPlugin` can be included manually in the `plugins` section of your configuration file. The plugin, combined with webpack's resolving of `import` and `export` statements, is what makes tree shaking possible. See the [production build](/guides/production) guide for more information.
Notice anything missing? The `square` function has been dropped and our output 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.


## 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`).
- Include a minifier that supports dead code removal (e.g. the `UglifyJSPlugin`).

If you are interested in more ways to optimize your output, please jump to the next guide for details on building for [production](/guides/production).

0 comments on commit 3fde143

Please sign in to comment.