什么是 Tree Shaking?
Tree Shaking 是一种通过静态分析技术移除 JavaScript 或 TypeScript 代码中未使用部分(即“死代码”)的优化手段。其名称源于“摇树”动作的比喻:通过“摇晃”代码树,让未被引用的部分像枯叶一样脱落,从而减小最终打包文件的体积,提升应用的加载速度和性能。
核心特性:
- 静态分析:依赖 ES6 Module 的静态语法(如
import
/export
),在代码不运行的情况下分析模块的依赖关系。 - 作用范围:可移除未使用的模块、函数、变量,甚至 JSON 数据中未引用的字段。
- 工具支持:由 Rollup 最早推广,现已成为 Webpack、Vite 等主流构建工具的标准功能。
技术依赖:
- ES6 模块语法:Tree Shaking 的实现基于 ES6 模块的静态结构,而 CommonJS 的动态
require()
语法无法支持。 - 抽象语法树(AST) :通过解析代码生成 AST,标记未被引用的节点,最终删除这些节点。
Tree Shaking 如何优化 JavaScript 打包体积?
优化机制详解:
-
静态分析依赖关系
构建工具(如 Webpack、Rollup)会扫描所有import
和export
语句,构建模块依赖图。例如:
// math.js
export function add(a, b) { return a + b; }
export function subtract(a, b) { return a - b; }
// app.js
import { add } from './math';
console.log(add(5, 3));
-
在此例中,
subtract
函数未被使用,会被标记为“死代码”。 -
标记与删除未引用代码
- 标记阶段:通过
usedExports
字段记录哪些导出值被其他模块引用。 - 删除阶段:结合代码压缩工具(如 Terser),将未标记的代码从 AST 中移除。
- 标记阶段:通过
-
副作用处理
- 副作用代码:指在导入时执行特定行为(如修改全局变量)的代码。例如:
// polyfill.js(有副作用)
if (!globalThis.Promise) {
globalThis.Promise = MyCustomPromise;
}
此类代码需通过 sideEffects: false
配置明确声明,否则可能被误删。
关键配置与实践:
-
启用条件:
- Webpack:生产环境默认启用;开发环境需配置
optimization.usedExports: true
。 - Rollup/Vite:默认支持,无需额外配置。
- Webpack:生产环境默认启用;开发环境需配置
-
最佳实践:
- 使用 ES6 模块语法:避免使用 CommonJS 或动态
import()
。 - 优化第三方库:优先选择支持 Tree Shaking 的库(如
lodash-es
替代lodash
)。 - 按需引入:避免全量导入,例如
import { Button } from 'antd'
而非import 'antd'
。
- 使用 ES6 模块语法:避免使用 CommonJS 或动态
-
局限性:
- 动态代码:如
eval()
或运行时决定的导入路径,无法被静态分析。 - CSS 处理:需配合
purgecss
等插件实现 CSS 的 Tree Shaking。
- 动态代码:如
工具对比与选型建议
工具 | Tree Shaking 支持 | 优势场景 |
---|---|---|
Webpack | 需配置 sideEffects 和 usedExports | 复杂应用,插件生态丰富 |
Rollup | 原生支持,优化效果最佳 | 库开发,ES 模块优先 |
Vite | 基于 Rollup,默认启用 | 现代框架(如 Vue/React) |
总结
Tree Shaking 通过静态分析和依赖图标记,精准移除未使用代码,是优化 JavaScript 打包体积的核心技术。其效果依赖于 ES6 模块语法、构建工具配置及代码结构的合理性。开发者在实践中需注意副作用处理、动态代码的规避,并结合工具特性(如 Webpack 的 sideEffects
或 Rollup 的原生优化)实现最佳效果。