什么是tree-shaking
tree-shaking
是一种Dead Code Elimination技术,它会在打包过程中静态分析模块之间的导入导出,确定哪些模块导出值没有被使用,并将其删除,从而实现了打包产物的优化。
tree-shaking
可以实现的基础
在之前CommonJs、AMD、CMD的模块化方案中,导入导出是高度动态的,难以预测的,因此在打包阶段,是无法分析哪些模块被使用,例如:
if(someTrue){
require('./bar');
exports.foo = 'foo';
}
而ES module方案下,模块之间的依赖关系是高度确定的,与运行状态无关,因此可以做到在编译时候 分析ESM的模块,可以从代码字面量中推断出哪些模块没有被使用,这就是tree-shaking
实现的必要条件。
tree-shaking
的作用示例
可以看到示例中,bar.js
中导出了bar``foo
,而只有bar
在index.js中使用过,foo
从未被用过,经过tree-shaking
处理后,foo
变量会被视作无用代码删除。
// index.js
import {bar} from './bar';
console.log(bar);
// bar.js
export const bar = 'bar';
export const foo = 'foo';
webpack的tree-shaking
实现步骤
webpack中实现
tree-shaking
总述: webpack 先标记出模块导出值中哪些没有被用过,然后使用Terser
删掉这些没用的。 具体过程:
- make阶段,收集模块导出,并记录到模块依赖关系图ModuleGraph 变量中
- Seal阶段, 遍历ModuleGraph,标记没有被用到过的导出。
- 生成产物阶段, 删除有标记的导出语句。
webpack给打了个什么样子的标记呢(unused harmony export XXX
)?看图:
webpack中最佳实践
虽然webpack自2.x就开始原生支持tree-shaking
,但是受限于js的动态特性,直至5.0依然没有解决需要代码副作用带来的问题,使得优化效果不如原本预想的那么完美,所以需要开发者有意识的优化代码结构,去帮助webpack更精准的检测无效代码。
- 避免无意义的赋值
- 使用
#pure
标注纯函数调用 - 禁止Bebal转译模块导入导出语句: Bebal 可以将 ESM风格模块,转成 CommonJs风格,Webpack无法对CommonJs风格
tree-shaking
- 优化导出值粒度:
export default
的值会被完整保留,比如:
export default {
bar: 'bar',
foo: 'foo'
}
应改成:
const bar = 'bar'
const foo = 'foo'
export {
bar,
foo
}
- 使用支持
tree-shaking
的包,比如使用lodash-es
替代lodash
webpack开启tree-shaking
的配置
在 Webpack 中,启动 Tree Shaking 功能必须同时满足三个条件:
- 使用 ESM 规范编写模块代码
- webpack配置
optimization.usedExports
为true
,启动标记功能。 - 启动代码优化功能,可以通过如下方式之一即可实现:
- 配置
mode = production
- 配置
optimization.minimize = true
- 提供
optimization.minimizer
数组 例如:
// webpack.config.js module.exports = { entry: "./src/index", mode: "production", devtool: false, optimization: { usedExports: true, }, };
- 配置