Tree shaking 是打包工具的静态分析优化,只保留实际使用的 ES 模块代码;对 CommonJS 无效,需用 lodash/debounce 或 lodash-es 等 ESM 兼容方案,并满足 type: "module"、mode: 'production' 及规范导出形式。

Tree shaking 本身不是 JavaScript 语言特性,而是现代打包工具(如 Webpack、Vite、Rollup)在构建时做的静态分析优化——它只保留你实际 import 并使用的代码,删掉没被引用的导出(export)。 它对 ES 模块(import/export)有效,对 require 或 CommonJS 无效。
为什么 import { debounce } from 'lodash' 还是引入了整个 lodash?
因为多数 npm 包(如 lodash)默认导出的是 CommonJS 格式,即使你写的是 ES import,打包工具也无法安全地做静态分析。而且 lodash 的主入口(node_modules/lodash/index.js)本身是动态拼接模块的,没有明确的 export 声明。
- ✅ 正确做法:用支持 ES 模块的子包,比如
import debounce from 'lodash/debounce'(注意路径,不是从根导入) - ✅ 或换用原生支持 ESM 的库,如
lodash-es:import { debounce } from 'lodash-es' - ❌ 错误假设:“只要写了
{}解构,tree shaking 就自动生效”——前提是模块本身导出方式可被静态分析
Webpack 中开启 tree shaking 需要哪些配置?
Webpack 5+ 默认启用 tree shaking,但有三个关键前提必须满足:
-
"type": "module"在项目package.json中(或所有源文件用.mjs后缀),确保 Webpack 把你的代码当 ES 模块处理 -
mode: 'production'—— development 模式下不会删除未使用代码(为调试保留) - 导出必须是
export const foo = ...或export function bar() {...}等具名/默认导出,不能是export default { foo, bar }这种对象字面量导出(它不可被静态判定哪些属性被用了)
如果你写了 export default { init: () => {}, destroy: () => {} },即使只调用 instance.init(),整个对象仍会被保留。
立即学习“Java免费学习笔记(深入)”;
Vite 或 Rollup 为什么 tree shaking 更“狠”?
它们原生基于 ES 模块设计,解析和剪枝更激进。例如:
export const a = 1
export const b = 2
export default function main() { return a + b }
若只 import main from './math.js',Vite 会保留 main 和它直接依赖的 a、b;但如果 main 内部没用到 b,而你又没显式 import b,那 b 就会被摇掉。
- ⚠️ 注意副作用:如果某模块有顶层执行逻辑(如
console.log('init')),且你没声明"sideEffects": false或对应模式,打包工具不敢删它——哪怕没被 import - 在
package.json中加"sideEffects": ["*.css"]表示只有 CSS 文件有副作用,其余 JS 全可安全 shake
真正卡住 tree shaking 的,往往不是配置开关,而是模块导出形式、第三方包格式、以及你是否无意中触发了副作用(比如 import 一个只用来注册全局插件的模块,却没告诉打包器“它没副作用”)。











