【Webpack】TreeShaking与静态分析

本文详细解释了Treeshaking的原理,它是如何通过ES6模块化和静态分析来消除未被引用的代码,以及在Webpack中如何设置sideEffects属性以增强TreeShaking效果。特别强调了生产环境下开启TreeShaking以优化项目性能的重要性。

TreeShaking

Tree shaking 字面意就是“摇树”,通过摇树将树上枯黄的叶子摇落。tree shaking 的作用是把项目中没必要的代码全部抖落掉,消除被引用,删除没被调用的无用模块代码,该优化最终实现的是代码体积的减少,也属于项目性能优化的一部分。
原理是利用 ES6 的模块化语法,通过静态分析代码之间的引用关系,来判断哪些模块未被引用,进而删除对应代码。

应用 tree shaking 需要具备条件:

  • 使用 ES2015 模块语法 —— import export
  • 确保没有编译器将的 ES2015 模块语法转换为 CommonJS 的(这是现在常用的 @babel/preset-env 的默认行为 --> 在 .babelrc 文件里将将 modules 设置为 false,避免将 ES Module 模块类型转换为CommonJS)。
  • 生产环境下默认开启。(将 mode 设置为 production 时,Tree Shaking 是默认开启的。production 模式自动启用了多种优化措施,包括代码压缩、作用域提升 Scope Hoisting 和 Tree Shaking,旨在减小最终bundle的体积和改善应用的性能)
  • 可以配置 sideEffects 属性(非开启必须条件,所有文章里都把它们放一起说。。),以帮助 Webpack 识别和移除那些包含副作用的模块或文件,进一步减少 bundle 大小。
// webpack.config.js

const path = require('path');
module.exports = {
  mode: 'production',
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist'),
  },
};


// package.json

{
  "name": "tree-shaking-demo",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "build": "webpack"
  },
  "sideEffects": false,
  "devDependencies": {
    "webpack": "^5.0.0",
    "webpack-cli": "^4.0.0"
  }
}

sideEffect

sideEffects 属性可能会影响 Tree Shaking 的有效性。
Tree Shaking 的工作原理是分析模块的导入和导出,并移除那些未被用到的导出。但是,如果一个模块执行了一些副作用(例如,修改全局状态、立即执行的 DOM 操作等),Webpack 将默认地保守处理,不会删除该模块,即使它的导出没有被其他模块使用。这是因为 Webpack 无法确定移除该模块是否会影响程序的运行。
当你指定 sideEffects 属性时,你在告诉 Webpack 哪些文件是“纯净”的(没有副作用),因此即使这些文件的内容没有被显式导入,Webpack 在 Tree Shaking 过程中也可以安全地移除它们。

  • 设置"sideEffects": false,告诉 Webpack 项目中的所有模块都没有副作用,因此,如果某个模块的导出未被使用,Webpack 会在生产构建时把它移除。
  • 设置"sideEffects": [“./src/someSideEffectfulFile.js”],如果只有部分文件有副作用,可以通过数组具体指明。
  • 没有设置 sideEffects 属性,Webpack 会更加保守地处理模块移除,可能会导致某些实际上可以安全移除的代码被保留在最终的 bundle 中。

静态分析

Tree Shaking 实现的关键得益于 ES Module 模块的静态分析。打包工具(如Webpack)分析应用程序中所有的 import 和 export 语句,构建起依赖图,这个图展现了模块之间的依赖关系。随后,遍历这个图,标记那些被导入但未被其他模块引用的导出。这些被标记的导出即为“未使用的代码”。在最终的打包过程中,打包工具会移除那些标记为未引用的导出。在 Webpack 中,这通常发生在代码压缩(minification)阶段,使用如 Terser 这样的压缩工具来消除死代码。
在 ES Module 中,我们可以将模块的加载分为两个阶段:静态分析和编译执行;静态分析,即在代码执行前就能对整体代码依赖调用关系等进行分析读取。
ES Module 特性:

  1. 只能作为模块顶层的语句出现(而不嵌套在条件语句中)
  2. import 的模块名只能是字符串常量,导入和导出语句没有动态部分(不允许使用变量、表达式等)

ES Module 的 import 语法完美可以使用 tree shaking,因为可以在代码不运行的情况下就能分析出不需要的代码。
不同于 ES Module,CommonJS 支持动态加载模块,在加载前是无法确定模块是否有被调用,所以并不支持 tree shaking。

参考:
https://mp.weixin.qq.com/s/SbJNbSVzSPSKBe2YStn2Zw
https://webpack.docschina.org/guides/tree-shaking/
https://segmentfault.com/a/1190000040037144

### 3.1 Tree Shaking 的工作原理 WebpackTree Shaking 是一种基于 ES6 模块系统的代码优化技术,其核心目标是识别并移除未使用的 JavaScript 代码,从而减小最终打包体积,提高应用加载性能。这一机制依赖于 ES6 模块的静态结构特性,即模块的导入和导出在编译时是确定的,这使得 Webpack 能够在不执行代码的情况下进行依赖分析[^4]。 Tree Shaking 的工作流程主要包括以下几个步骤: 1. **依赖收集**:Webpack 通过静态分析 `import` 和 `export` 语句,构建模块之间的依赖图。 2. **标记未使用代码**:在构建过程中,Webpack 标记那些没有被引用的导出内容。 3. **删除无用代码**:在最终输出的打包文件中,这些未被使用的代码将被移除,从而实现“摇树”优化效果[^3]。 该技术最初源自 Rollup 打包工具,后被 Webpack 2 引入并广泛应用于现代前端项目中。合理使用 Tree Shaking 可使 JavaScript 打包体积减少 30%-60%,显著提升加载速度和用户体验[^2]。 ### 3.2 Tree Shaking 的启用条件 要使 Tree Shaking 正常工作,项目必须满足以下条件: - 使用 ES6 的 `import` 和 `export` 语法,避免使用 CommonJS 的 `require()`。 - 设置 `mode: 'production'`,因为 Webpack 在生产模式下默认启用代码压缩和 Tree Shaking。 - 使用支持 Tree Shaking 的构建工具版本(Webpack 4 及以上)。 ### 3.3 Webpack 中的 Tree Shaking 配置优化 为了最大化 Tree Shaking 的优化效果,需在 Webpack 配置中进行如下设置: #### 3.3.1 设置 `mode: 'production'` Webpack 在生产模式下会自动启用 `TerserPlugin` 和 `usedExports` 选项,用于删除未使用的导出代码。 ```javascript module.exports = { mode: 'production' }; ``` #### 3.3.2 启用 `usedExports` 优化 在 Webpack 配置的 `optimization` 部分中,启用 `usedExports` 可以帮助识别哪些导出未被使用。 ```javascript module.exports = { optimization: { usedExports: true } }; ``` #### 3.3.3 使用 `TerserPlugin` 进行代码压缩 `TerserPlugin` 是 Webpack 默认使用的 JavaScript 压缩工具,它可以在压缩过程中进一步移除未使用的代码。 ```javascript const TerserPlugin = require('terser-webpack-plugin'); module.exports = { optimization: { minimizer: [new TerserPlugin()] } }; ``` #### 3.3.4 配置 `package.json` 中的 `sideEffects` 为了帮助 Webpack 更准确地判断哪些模块可以安全地删除,可以在 `package.json` 中设置 `"sideEffects": false`。如果某些文件具有副作用(如全局样式或 polyfill),则应将其路径列出。 ```json { "sideEffects": false } ``` 或者: ```json { "sideEffects": ["./src/some-polyfill.js"] } ``` ### 3.4 实践建议注意事项 - **避免动态导入**:Tree Shaking 无法处理动态导入(如 `import(path + 'module.js')`),因为其依赖关系无法在编译时确定。 - **使用模块化的第三方库**:选择支持 ES6 模块并提供按需引入的库(如 Lodash 的 `lodash-es`),以提高 Tree Shaking 的效果。 - **定期分析打包体积**:使用 Webpack Bundle Analyzer 插件分析打包结果,识别未被有效摇除的代码。 ```bash npm install --save-dev webpack-bundle-analyzer ``` ```javascript const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer'); module.exports = { plugins: [new BundleAnalyzerPlugin()] }; ``` 通过上述配置和优化策略,可以充分发挥 Webpack Tree Shaking 的潜力,实现更小的打包体积和更高效的前端构建流程。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值