Tree Shaking 依赖 ES Module 静态语法,需避免 CommonJS、顶层副作用、全量导入、默认导出对象及动态 export;须通过 stats、产物分析和死代码验证生效。

Tree Shaking 依赖“静态可分析”的模块语法
Tree Shaking 的核心前提是:打包工具(如 Webpack、Rollup、Vite)能通过静态分析识别哪些 export 没有被 import,从而安全剔除。这意味着必须使用 ES Module(import/export)语法,而非 CommonJS(require/module.exports)。CommonJS 是动态的,无法在构建时确定依赖关系,会导致 Tree Shaking 失效。
避免副作用导致代码被意外保留
即使某个模块未被显式引入,只要它存在**顶层副作用**(如执行全局赋值、修改原型、发起请求、绑定事件等),打包工具默认不敢删除它——因为无法判断删除是否影响运行时行为。保障正确性的关键操作包括:
- 在
package.json中声明"sideEffects": false(表示整个包无副作用),或更精确地列出有副作用的文件(如["*.css", "src/init.js"]) - 确保工具库的导出是纯函数或具名常量,避免在
export外部执行逻辑 - 将有副作用的初始化代码显式封装(例如写成
init()函数),由业务代码按需调用,而非自动执行
警惕常见“破坏 Tree Shaking”的写法
以下模式会让导入的模块无法被摇掉,即使只用了其中一小部分:
-
import \* as utils from './utils.js'—— 全量导入会阻断对单个导出的分析 -
import { foo } from './utils.js'; const x = utils.bar;—— 即使只解构了foo,后续又通过命名空间对象访问其他成员,会导致整个模块保留 -
export default { a: 1, b: () => {} }—— 默认导出对象后,工具通常无法判断哪些属性被真正使用(除非使用支持该优化的工具如 Rollup + 启用treeshake.moduleSideEffects) - 在
export中使用计算属性、条件表达式或动态键(如export const [key] = value)—— 破坏静态性
验证与调试 Tree Shaking 是否生效
不能仅凭配置就认定成功。实际保障需主动验证:
- 启用打包工具的
stats输出(如 Webpack 的stats: "verbose"或 Vite 的build.report),查看usedExports和providedExports字段 - 生成并分析产物(如用
rollup --bundleConfigAsCjs --format es+node_modules/.bin/rollup-plugin-visualizer)观察模块引用图 - 在源码中临时添加一个明显无用的导出(如
export const DEAD_CODE = 'xxx';),检查它是否出现在最终 bundle 中 - 注意开发环境默认不启用完整 Tree Shaking(尤其 HMR 场景),务必在生产构建下测试
Tree Shaking 不是魔法,它是基于约束的静态优化。写得越“规矩”,摇得越干净;越想绕过规则,越容易留下冗余甚至引发运行时问题。











