Webpack 学习笔记 (五 - Tree Shaking)

Tree Shaking 是一个术语,通常用于描述移除 JavaScript 上下文中的死代码。它依赖于 ES2015 模块语法的 静态结构 特性,例如 import 和 export。这个术语和概念实际上是由 ES2015 模块捆绑器 rollup 普及起来的。

提示

死代码(dead code)是指程序中一段已经不会被执行的代码,通常是因为重构、优化或者逻辑错误导致的。这些代码可能是之前版本的遗留物,或者某些条件下永远不会被执行的代码。

webpack 2 正式版本内置支持 ES2015 模块(也叫做 harmony module)与对未使用模块的检测能力。webpack 4 正式版本扩展了此检测能力:通过 package.json 的 "sideEffects" 属性作为标记,向编译器提供提示,表明项目中的哪些文件是纯正的 ES2015 模块,由此可以安全地删除文件中未使用的部分。

将函数标记为 side-effect-free

package.json 的 "sideEffects" 属性可以保障代码的纯粹性。

{
  "name": "your-project",
  "sideEffects": false
}

如果代码有些部分无作用,也可以将 "sideEffects" 设置为数组:

{
  "name": "your-project",
  "sideEffects": ["./src/some-side-effectful-file.js"]
}

此数组支持简单的 glob 模式匹配相关文件。其内部使用的是 glob-to-regexp(支持:***{a,b}[a-z])。如果匹配模式为 *.css,且不包含 /,将被视为 **/*.css

提示

注意,所有导入文件都会受到 tree shaking 的影响。这意味着,如果在项目中使用类似 css-loader 的东西并导入了一个 CSS 文件,则需要将其添加到副作用列表中表示其存在副作用,以免在生产模式中无意中将它删除:

{
  "name": "your-project",
  "sideEffects": ["./src/some-side-effectful-file.js", "*.css"]
}

 最后,还可以在 module.rules 配置选项 中设置 "sideEffects"

Tree Shaking 和 sideEffects

sideEffects 和 usedExports(更多地被称为 tree shaking)是两种不同的优化方式。

sideEffects 更为有效 是因为它允许跳过整个模块/文件和整个文件子树。

usedExports 依赖于 terser 检测语句中的 side effect。它是一个 JavaScript 任务而且不像 sideEffects 一样简单直接。并且由于规范认为副作用需要被评估,因此它不能跳过子树 / 依赖项。尽管导出函数能正常运行,但 React 的高阶组件在这种情况下会出问题。

将函数调用标记为无 side effect

通过 /*#__PURE__*/ 注释可以告诉 webpack 某个函数调用无副作用。它可以被放到函数调用之前,用来标记此函数调用是无副作用的。传入函数的参数无法被刚才的注释所标记,需要单独对每一个参数进行标记。如果一个没被使用的变量定义的初始值被认为是无副作用的,它会被标记为死代码,不会被执行且会被压缩工具清除掉。当 optimization.innerGraph 被设置成 true 时这个行为将被启用。

file.js

/*#__PURE__*/ double(55);

压缩输出结果

通过 import 和 export 语法,我们已经找出需要删除的死代码,然而,不仅仅是要找出,还应在 bundle 中删除它们。为此,我们需要将 mode 配置选项设置为 production

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',
};

提示

注意,也可以在命令行接口中使用 --optimize-minimize 标记启用 TerserPlugin

准备就绪后运行命令 npm run build,看看输出结果有没有发生改变。

你发现 dist/bundle.js 中的差异了吗?现在整个 bundle 都已经被压缩和混淆破坏,但是如果仔细观察,则不会看到引入了 square 函数,但能看到 cube 函数的破坏版本(function r(e){return e*e*e}n.a=r)。现在通过代码压缩与 tree shaking,我们的 bundle 缩小了几个字节!虽然在这个特定示例中,可能看起来没有减少很多,但是,在有着复杂依赖树的大型应用程序上运行 tree shaking 时,会对 bundle 产生显著的体积优化。

提示

在使用 tree shaking 时必须有 ModuleConcatenationPlugin 的支持,可以通过设置配置项 mode: "production" 启用它。如果没有这么做,那么需要手动引入 ModuleConcatenationPlugin

总结

为了利用 tree shaking 的优势,必须:

  • 使用 ES2015 模块语法(即 import 和 export);
  • 确保没有编译器将 ES2015 模块语法转换为 CommonJS(顺带一提,这是现在常用的 @babel/preset-env 的默认行为,请参阅 文档 以了解更多信息)。
  • 在项目的 package.json 文件中添加 "sideEffects" 属性。
  • 使用 mode 为 "production" 的配置项以启用 更多优化项,包括压缩代码与 tree shaking。

可以将应用程序想象成一棵树。绿色表示实际用到的源码和库,是树上活的树叶。灰色表示未引用代码,是秋天树上枯萎的树叶。为了除去死去的树叶,你必须摇动(shake)这棵树,使它们落下。

  • 16
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值