Tree Shaking 仅在 ES Module 下生效,因其静态可分析性支持构建时识别未使用的 export;CommonJS 动态特性使其无法实现。需用命名导出、避免副作用、启用工具生产模式,并防止 Babel 转译破坏 ESM 结构。

Tree Shaking 能在 JavaScript 中生效,核心前提是使用 ES Module(ESM)语法(import/export),因为 ESM 的模块依赖关系是静态可分析的——打包工具(如 Webpack、Rollup、Vite)能在构建时扫描源码,识别出哪些 export 从未被 import,从而安全地剔除它们。
为什么只有 ES Module 支持 Tree Shaking?
CommonJS(require/module.exports)是运行时动态加载,导出对象可能被任意修改(比如 exports.foo = condition ? a : b),工具无法在编译期确定哪些导出实际被用到。而 ESM 要求 export 和 import 必须是顶层语句、不能条件化,导出标识符必须显式声明,这为静态分析提供了确定性基础。
ESM 下 Tree Shaking 生效的关键条件
-
使用命名导出(named export)而非默认导出(default export):例如
export const foo = ...和import { foo } from './utils',工具才能精确识别并剔除未引用的bar;若全部用export default { foo, bar },则整个对象常被视为“已使用”,无法摇掉内部属性。 -
避免副作用干扰:如果模块顶层有执行逻辑(如直接调用函数、修改全局变量),即使没被 import,也可能被保留。可通过
/*#__PURE__*/注释标记纯函数调用,或在打包配置中设置sideEffects: false(需确保项目确实无副作用)。 -
确保构建工具启用相关功能:Webpack 需开启
mode: 'production'(自动启用optimization.usedExports和concatenateModules);Rollup 默认深度分析;Vite 基于 Rollup,开发时也做初步摇树。
一个典型对比示例
✅ 可被摇掉:
/* math.js */
export const add = (a, b) => a + b;
export const multiply = (a, b) => a * b;
<p>/<em> main.js </em>/
import { add } from './math.js';
console.log(add(1, 2)); // multiply 不会被打包❌ 摇不掉(破坏静态分析):
立即学习“Java免费学习笔记(深入)”;
/* utils.js */
const helpers = {
foo() {},
bar() {}
};
export default helpers;
<p>/<em> main.js </em>/
import utils from './utils.js';
utils.foo(); // 工具无法确认 bar 是否被间接调用,整个对象保留注意:Babel 和转译的影响
如果使用 Babel 将 ESM 转为 CommonJS(如 @babel/preset-env 默认行为),会丢失 ESM 的静态结构,导致 Tree Shaking 失效。解决方案包括:
– 设置 modules: false(让 Babel 不处理模块语法,交由打包工具处理);
– 使用支持 ESM 输出的插件(如 @rollup/plugin-babel);
– 直接使用现代构建工具(Vite、esbuild),它们原生处理 ESM,无需额外转译。


















