Tree Shaking 是构建阶段基于 ES 模块静态结构的死代码消除技术,只移除未被 import 引用的导出成员,不依赖运行时执行;它通过依赖图标记“已使用”导出,由 Terser 在压缩阶段物理删除未标记代码,并需正确配置 sideEffects 才能安全生效。

Tree Shaking 中的“死代码消除”不是靠运行时判断,而是构建阶段基于静态结构做的精准裁剪。它不关心某段代码“会不会执行”,只关心“有没有被 import 引用”。只要没被显式引入,哪怕逻辑上完全合法、语法上完全正确,也会被当作死代码剔除。
死代码在这里特指“未被引用的导出成员”
ES6 模块的 import/export 是静态声明,构建工具能提前知道:哪些函数、常量、类被别的文件 import 了,哪些完全没有被碰过。例如:
-
utils.js导出了add和subtract -
main.js只写了import { add } from './utils.js' - 那么
subtract就是死代码——不是因为它写错了,而是它在整个依赖图中“没被连上”
它和传统编译器的 DCE 有本质区别
传统 DCE(Dead Code Elimination)处理的是“不可达代码”,比如 return 后面的语句、if (false) 里的块。而 Tree Shaking 处理的是“未连接代码”——即使这段代码百分百会执行(比如模块顶层的 console.log),只要它属于一个完全没被 import 的模块,且该模块被判定为无副作用,就可能被整个删掉。
- DCE 关注控制流是否可达
- Tree Shaking 关注模块图中是否有引用边
- 两者目标一致(减体积),但分析维度完全不同
真正删除发生在压缩阶段,不是打包阶段
Webpack 或 Vite 这类工具只负责“标记”:从入口出发,顺着 import 往下遍历,把所有触达的 export 打上“used”标签;其余未标记的 export 就进入“待摇名单”。但它们不会直接删代码——那是 Terser 的事。Terser 在 minify 阶段读取这些标记,把未使用的函数、变量、甚至整块模块定义,从 AST 中物理移除。
立即学习“Java免费学习笔记(深入)”;
- 没启用 production 模式 → Terser 不工作 → 即使标记了也不删
- 用了 CommonJS(require)→ 无法静态分析 → 标记失效
-
sideEffects: true(默认)→ 工具不敢删任何模块 → 摇不动
副作用配置是安全删除的前提
有些模块虽然没被 import,但执行时会改全局、插 DOM、监听事件。这种“静默影响”叫副作用。如果构建工具误删了它,程序就崩了。所以必须通过 package.json 的 sideEffects 字段明确告诉工具:“哪些文件可以放心删”。设为 false 表示全项目无副作用;设为数组(如 ["*.css", "src/init.js"])表示只有列出的文件有副作用,其余可删。
- 没配
sideEffects→ 默认保守策略 → 不删任何模块 → Tree Shaking 形同虚设 - 配错(比如漏写 CSS 文件)→ 样式丢失
- 第三方库若没申明
sideEffects,其内部未引用的模块也无法被摇掉


















