Tree Shaking 能移除未使用代码,前提是使用静态的 ES 模块语法、避免副作用、启用生产环境压缩,并确保工具链不转译为 CommonJS;动态 import 和顶层执行逻辑会阻碍其生效。

Tree Shaking 能在打包时移除未使用的代码,核心前提是 JavaScript 模块必须是 ES 模块(ESM),因为只有 ESM 的 import/export 是静态的、可提前分析的。CommonJS(require/module.exports)是运行时动态的,无法在构建阶段确定哪些导出没被使用,所以不支持 Tree Shaking。
为什么 ES 模块支持静态分析
ES 模块的导入导出语句必须出现在顶层,且路径和绑定名在代码解析阶段就能确定,不能放在条件分支或函数里。例如:
import { foo, bar } from './utils.js'<br>export const a = 1<br>export function b() {}
打包工具(如 Webpack、Rollup、Vite)在构建时通过 AST 解析这些语句,构建模块依赖图,再从入口开始追踪哪些 export 真正被 import 引用过。没被引用的导出就标记为“可删除”。
Tree Shaking 生效的关键条件
-
使用
import/export语法:不能混用 CommonJS,否则整个模块会被当作黑盒处理 -
避免副作用干扰:如果模块顶层有执行逻辑(比如直接调用函数、修改全局变量),打包工具默认认为它有副作用,不会删掉整个模块。可通过
/*#__PURE__*/注释标记纯函数调用,或在package.json中声明"sideEffects": false或精确列出有副作用的文件 -
生产环境 + 启用压缩:Webpack 需要
mode: 'production';Rollup/Vite 默认开启;Terser 等压缩器会真正擦除未引用的export和相关代码 -
避免动态 import 的副作用误判:
import('./xxx.js')是动态的,无法用于静态依赖分析,但它的模块仍可能被 Tree Shaking —— 前提是该模块本身用的是 ESM 且导出未被其他静态 import 使用
一个典型失效案例及修复
写法错误(破坏静态性):
// utils.js<br>export const A = 1<br>export const B = 2<br><br>// main.js<br>const module = 'utils'<br>const { A } = await import(`./${module}.js`) // 动态路径 + await → 无法静态分析导出使用情况
修复方式:
- 改用静态
import:如果只用A,就写import { A } from './utils.js' - 确保工具链支持:检查 Babel 是否把
export编译成 CommonJS(需关闭@babel/preset-env的modules转换,或设为false) - 验证输出:查看打包后的 bundle,搜索未使用的导出名是否还存在;也可用 webpack-bundle-analyzer 可视化依赖
实际项目中怎么确认它在工作
最直接的方式是故意导出一个没被引用的函数,然后构建并检查产物:
// math.js<br>export function add(a, b) { return a + b }<br>export function unused() { console.log('never called') } // ← 这个应该消失
构建后搜索 unused,若 bundle 里找不到,说明 Tree Shaking 生效。如果还在,就检查是否触发了副作用保护(比如 unused 被当成有副作用)、是否被间接引用(如通过字符串 key 访问)、或是否模块被其他地方静态 import 了整个命名空间(import * as math from './math.js')。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











