概念
tree shaking,也称摇树。
当你引入一个模块时,你可能用到的只是其中的某些功能,这个时候,我们不希望这些无用的代码打包到项目中去。通过tree-shaking,就能删除无用代码,以达到减小体积,缩短 http 请求时间,起到一定效果的页面优化
触发条件
开启 tree-shaking,需要满足以下三个条件:
使用es module的模块规范
tree-shking建立在es module静态分析的基础之上,所以代码必须使用esm的规范。业务代码一般都会使用esm,但是引入的第三方依赖就不一定了。比如lodash就是commonjs规范的,直接使用lodash是不会触发treeshking的,解决方案就是使用lodash的esm版本lodash-es。
package.json中的main字段是node package的入口,但是是commonjs规范的。想要使用treeshking的功能必须使用esm的入口,所以rollup(最早的treeshking实现)提出了module字段的提案,在这里配置es module的入口,这种约定虽然还没有成为规范,但已经被很多包所实现了。比如vue的package.json
如果你在使用Babel,这一点可能已让你遇到麻烦了。因为Babel的预置默认把任何模块转译成CommonJS模块。你可以简单设置modules: false来解决此问题,在.babalrc或者webpack.config.js中设置都可以
开启 optimization.usedExports
optimization.providedExports配置能使 webpack 确定每个模块导出项(exports)的使用情况。然后optimization.usedExports收集到的信息会被其他优化项或产出代码使用到(模块未用到的导出项不会被导出,在语法完全兼容的情况下会把导出名称混淆为单个char)。为了最小化代码体积,未用到的的导出项目(exports)会被删除。
webpack4 默认production模式会开启 optimization.usedExports。
module.exports = {
//...
optimization: {
usedExports: true
}
};
所以:
webpack4中,如果一些模块导出了一个对象,用到这个模块的地方只使用了某几个方法,其余的方式是不能被treeshking的。原因也是因为编译期间的静态分析只能对es module的相关语法做分析,是不会真正去执行代码的。
使用压缩插件
webpack4 默认production模式会开启压缩插件
如何控制范围?
我们知道,treeshaking 就是有副作用时(引入但未使用),打包时会删掉这些无效引用。但某些情况下,我们又不能删掉,这就需要我们精准控制了
- 通过 package.json 的
"sideEffects"属性,控制是否有副作用文件
{
...
"sideEffects": false // 整个项目都没副作用,也就是说,会全部删掉无效引用
}
注意,任何导入的文件都会受到 tree shaking 的影响。这意味着,如果在项目中使用类似 css-loader 并导入 CSS 文件,则需要将其添加到 side effect 列表中,以免在生产模式中无意中将它删除:
{
"name": "webpack-demo",
"sideEffects": [
"*.css" // *.css文件有副作用,不能直接删掉
]
}
- 通过注释标记函数为无副作用函数。如果一个没被使用的变量定义的初始值被认为是无副作用的(pure),它会被标记为死代码,不会被执行且会被压缩工具清除掉。
/*#__PURE__*/ double(55);
webpack5 中的tree-shaking
webpack5 以前,tree-shaking 比较简单,主要是找import进来的变量是否在这个模块内出现过,出现过则不剔除,否则剔除,并且用于 esModule 中
webpack5,可以进行根据作用域之间的关系进行优化。如:
a.js 中定义了两个方法 x 和 y,在 index.js 中引入了 a.js 的 x 方法。那么 webpack4 打包出来的结果包含了 index.js 和 a.js 的内容,包含了没有用到的 y 方法。但是 webpack5 的 treeshaking,会进行作用域分析,打包结果只有 index 和 a 文件中的 a 方法,没有用到的 b 方法是不会被打包进来的。
注意:如果引用了但是并未使用, tree-shaking 也会剔除掉
原理
还没了解,先挂着
Tree-Shaking性能优化实践 - 原理篇