webpack之treeShaking
使用 Tree-Shaking 删除多余模块导出
Tree-Shaking
较早前由 Rich Harris 在 Rollup 中率先实现,Webpack 自 2.0 版本开始接入,是一种基于 ES Module 规范的 Dead Code Elimination 技术,它会在运行过程中静态分析模块之间的导入导出,判断哪些模块导出值没有被其它模块使用 —— 相当于模块层面的 Dead Code
,并将其删除.
Webpack 中,Tree-shaking
的实现,一是需要先 「标记」
出模块导出值中哪些没有被用过;二是使用代码压缩插件 —— 如 Terser
删掉这些没被用到的导出变量。
在 Webpack 中,启动 Tree Shaking 功能必须同时满足三个条件:
- 使用 ESM 规范编写模块代码;
- 配置 optimization.usedExports 为 true,启动标记功能;
- 启动代码优化功能,可以通过如下方式实现:
- 配置 mode = production;
- 配置 optimization.minimize = true ;
- 提供 optimization.minimizer 数组。
// webpack.config.js
module.exports = {
mode: "production", // 生产模式自动开启
optimization: {
usedExports: true,
},
};
在 CommonJs、AMD、CMD 等旧版本的 JavaScript 模块化方案中,导入导出行为是高度动态,难以预测的;比如将
require()
语句写在判断逻辑中。而 ESM 方案要求所有的导入导出语句只能出现在模块顶层,模块之间的依赖关系是高度确定的,与运行状态无关。
最佳实践
- 始终使用ESM导入模块
- 使用 #pure 标注纯函数调用,JavaScript 中的函数调用语句也可能产生副作用,因此默认情况下 Webpack 并不会对函数调用做 Tree Shaking 操作。不过,开发者可以在调用语句前添加
/*#__PURE__*/
备注,明确告诉 Webpack 该次函数调用并不会对上下文环境产生副作用。 - 禁止 Babel 转译模块导入导出语句,@babel/preset-env设置modules为commonjs是编译成commonjs风格的代码,webpack无法做模块静态分析.
- 使用支持 tree-shaking 的包,如使用lodash-es替代lodash
综上,Tree-Shaking 是一种只对 ESM 有效的 Dead Code Elimination 技术,它能够自动删除无效(没有被使用,且没有副作用)的模块导出变量,优化产物体积。不过,受限于 JavaScript 语言灵活性所带来的高度动态特性,Tree-Shaking 并不能完美删除所有无效的模块导出,需要我们在业务代码中遵循若干最佳实践规则,帮助 Tree-Shaking 更好地运行。
使用 Scope Hoisting 合并模块
const ModuleConcatenationPlugin = require('webpack/lib/optimize/ModuleConcatenationPlugin');
module.exports = {
// 方法1: 将 `mode` 设置为 production,即可开启
mode: "production",
// 方法2: 将 `optimization.concatenateModules` 设置为 true
optimization: {
concatenateModules: true,
usedExports: true,
providedExports: true,
},
// 方法3: 直接使用 `ModuleConcatenationPlugin` 插件
plugins: [new ModuleConcatenationPlugin()]
};