webpack 摇树处理 Tree Shaking
摇树处理 Tree Shaking 是一项在消除 Javascript 上下文中死代码的技术。 它依赖于ES2015模块语法的静态结构,即 import 和 export。这个专业名词被 ES2015 module bundler rollup 所汇总推广了。
webpack 2版本自带内置支持ES2015模块(alias harmony modules 别名和声模块)以及未使用的模块导出检测。
新的webpack 4版本扩展了这一功能,通过 package.json 的 sideEffects 属性向编译器提供提示,表示项目中的哪些文件是“pure 纯粹的”,因此如果未使用则可以安全修剪。
修改项目
webpack-demo
|- package.json
|- webpack.config.js
|- /dist
|- bundle.js
|- index.html
|- /src
|- index.js
+ |- math.js
|- /node_modules
# src/math.js
export function square(x) {
return x * x;
}
export function cube(x) {
return x * x * x;
}
# webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
- }
+ },
+ mode: 'development',
+ optimization: {
+ usedExports: true
+ }
};
# src/index.js
- import _ from 'lodash';
+ import { cube } from './math.js';
function component() {
- var element = document.createElement('div');
+ var element = document.createElement('pre');
- // Lodash, now imported by this script
- element.innerHTML = _.join(['Hello', 'webpack'], ' ');
+ element.innerHTML = [
+ 'Hello webpack!',
+ '5 cubed is equal to ' + cube(5)
+ ].join('\n\n');
return element;
}
document.body.appendChild(component());
记住,现在并没有 import square 方法。 这段代码就是死代码, 没有没使用的 export 代码应该被裁剪掉.现在运行 npm run build, 并检查 不能 bundle 文件:
# dist/bundle.js (around lines 90 - 100)
/* 1 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
'use strict';
/* unused harmony export square */
/* harmony export (immutable) */ __webpack_exports__['a'] = cube;
function square(x) {
return x * x;
}
function cube(x) {
return x * x * x;
}
});
你会注意到没有 imprt 的 square 方法,仍然被捆绑在bundle中,并标有 unused harmony export square 备注代码未被导入。我们将在下一节中解决这个问题。
设置 sideEffects 选项
在 100% 的 ESM 模块世界中,识别副作用很简单。 但是,我们还没有,所以在同时,有必要为 webpack 的编译器提供关于代码“pureness 纯度”的提示。
方法是,向 package.json 添加 sideEffects 属性:
# package.json
{
"name": "your-project",
"sideEffects": false
}
上面提到的所有代码都不包含副作用,因此我们可以简单地将该属性标记为 false ,以通知 webpack 它可以安全地修剪未使用的导出。
tips:
“副作用”的定义是,在导入时执行特殊行为的代码,而不是公开一个或多个导出。 一个例子是 polyfill ,它影响全局范围,通常不提供导出。
如果您的代码确实有一些副作用,可以提供一个数组:
# package.json
{
"name": "your-project",
"sideEffects": [
"./src/some-side-effectful-file.js"
]
}
这个参数接受相对,绝对路径,或全局变量的数字作为键值。使用的方式是 micromatch。
请注意,任何导入的文件都会受到树抖动的影响。 这意味着如果在项目中使用类似css-loader的东西并导入 CSS 文件,则需要将其添加到副作用列表中,这样就不会在生产模式下无意中丢失代码:
# package.json
{
"name": "your-project",
"sideEffects": [
"./src/some-side-effectful-file.js",
"*.css"
]
}
最后,"sideEffects" 也可以设置在 module.rules 的选项中。
最小化输出 Minify the Output
所以我们通过使用import和export语法来提取我们的“死代码”,但我们仍然需要从捆绑中删除它。 为此,请将模式配置选项设置为生产配置选项。
# webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
- mode: 'development',
- optimization: {
- usedExports: true
- }
+ mode: 'production'
};
我们可以运行npm run build,看看是否有任何变化。
注意 dist/bundle.js 有什么不同吗?
很明显,整个捆绑包现在缩小并混淆了,但是,如果你仔细观察,你将看不到包 含 原 squre 方法,但会看到一个混淆版本的squre 方法(funtion r(e){return e * e * e}NA = R)。 随着缩小和树摇动,我们的捆绑现在变小了几个字节! 虽然在这个人为的例子中看起来似乎并不多,但是当处理具有复杂依赖树的较大应用程序时,效果将更加的明显。
tips:
树摇动工作需要 ModuleConcatenationPlugin,可以通过把mode切换为production生效。
也可以手动添加 ModuleConcatenationPlugin。
总结
要想使用 tree shaking, 你必须:
- 使用 ES2015 module 语法
import和export。 - 确保没有编译器将您的 ES2015 模块语法转换为 CommonJS 模块,(这是流行的
Babel预设@babel/preset-env的默认行为, 有关更多详细信息,请参阅文档。 - 向 package.json 添加 sideEffects 属性。
- 使用生产模式配置选项启用各种优化(mode: "production"),包括缩小和树抖动。
如果您对更多优化输出的方法感兴趣,请跳至下一个指南,了解有关生产的详细信息。