Tree Shaking 是一种基于 ES6 模块静态分析的构建优化技术,通过标记并剔除未使用的代码(死代码)来减小打包体积,其核心原理依赖于静态模块结构、构建工具的标记-剔除流程以及副作用配置,需在生产模式下结合压缩工具生效。
1. 基于 ES6 模块的静态结构- 静态依赖分析:ES6 模块(import/export)的依赖关系在编译时确定,无法动态修改(如不能在 if 语句中使用 import)。这种静态特性允许构建工具在不运行代码的情况下,通过解析模块的 import 和 export 语句,构建完整的依赖关系图。
- 排除动态模块:CommonJS 模块(require/module.exports)的依赖关系是动态的(例如运行时决定加载的模块),因此无法进行静态分析,Tree Shaking 不支持 CommonJS。
- 安全移除未使用代码:若某个 export 未被任何模块引用,构建工具会将其标记为“未使用”,并在后续步骤中移除。例如:// math.jsexport const add = (a, b) => a + b;export const subtract = (a, b) => a - b;// main.jsimport { add } from './math.js';console.log(add(2, 3));此例中,subtract 未被引入,启用 Tree Shaking 后不会被打包。
2. 标记与剔除未使用代码构建工具(如 Webpack、Rollup)通过两阶段流程实现 Tree Shaking:
- 标记阶段(Mark):从入口文件出发,递归遍历所有 import 的模块,记录被使用的 export。例如,若 main.js 引入了 math.js 的 add,则 add 被标记为“已使用”,而 subtract 未被标记。
- 剔除阶段(Sweep):未被标记的代码被视为“死代码”,在最终打包时被排除。此过程需结合压缩工具(如 Terser)真正移除代码。
3. 构建工具的配合与副作用配置4. 生产模式与压缩工具支持- 仅在生产模式生效:开发模式下为快速启动,通常跳过深度优化(如 Tree Shaking)。生产模式会启用更严格的优化策略。
- 依赖压缩工具:构建工具标记死代码后,需通过 UglifyJS、Terser 等工具进一步删除未引用的函数和变量。例如,Terser 会移除未被调用的函数定义。
关键点总结- 模块系统选择:必须使用 ES6 模块(import/export),避免 CommonJS。
- 副作用管理:通过 package.json 的 sideEffects 字段明确声明副作用,防止误删。
- 构建环境配置:在生产模式下启用,并配合压缩工具完成最终代码移除。
- 工具支持:常见于 Webpack、Rollup、Vite 等现代构建工具。
Tree Shaking 的有效性依赖于耐或笑静态分析的准确性,正确配置模块系统和副作用是关键。忽略细节(如误用 CommonJS 或未配置 sideEffects)可能导致优化失效或运行时错误。