Tree Shaking 是基于 ES6 模块静态分析剔除未使用导出的优化机制,依赖代码写法因其仅能识别静态 import/export;动态导入、默认导出对象、CommonJS 混用、副作用未声明等写法会破坏静态可分析性,导致失效。

Tree Shaking 是什么,为什么它依赖代码写法
Tree Shaking 不是 Webpack 或 Vite 自动“猜”出哪些函数没用,而是基于 ES 模块的静态结构(import/export 语法必须是静态的)在编译时剔除未被引用的导出。如果代码写法破坏了这种静态可分析性,比如动态导入、赋值给变量、条件导出、CommonJS 混用,Tree Shaking 就会失效。
确保模块导出和导入符合 ES Module 规范
这是 Tree Shaking 起效的前提:
- 用 export const / export function / export default 显式导出,避免
export = {}或module.exports = - 导入时用 named import(如
import { foo } from './utils'),而不是import * as utils或import utils from './utils'(后者需默认导出且无副作用才安全) - 避免在
export前做运行时判断,例如:const shouldExport = Math.random() > 0.5;<br>export const util = shouldExport ? () => {} : undefined;→ 这会让打包器无法判定是否可删
减少副作用,显式声明 “sideEffects: false”
即使某个模块没被 import,只要它有副作用(如执行 console.log、修改全局变量、调用第三方 init 方法),打包器就认为它“不能删”。要让 Tree Shaking 更激进:
- 把纯计算逻辑和副作用逻辑拆开,副作用代码单独组织(如放到
init.js中显式调用) - 在
package.json中设置 "sideEffects": false(表示整个包无副作用);若只有部分文件有副作用,写成数组形式,如"sideEffects": ["./src/polyfill.js", "*.css"] - 避免在模块顶层写执行语句,例如:
console.log('I am loaded');或localStorage.setItem('loaded', '1');
避免常见“反 Tree Shaking”写法
这些看似无害的写法,实际会让整块模块保留在最终包中:
-
default export 包裹对象:
export default { foo, bar }→ 改为 named export:export { foo, bar } -
重命名后导出:
import { foo as myFoo } from './a'; export { myFoo }→ 大多数工具链仍支持,但某些旧版 Rollup 可能不识别别名链,建议直接export { foo as myFoo } -
导出时解构再合并:
const { a, b } = utils; export { a, b }→ 破坏静态结构,改为直接export { a, b } from './utils' -
动态属性访问或反射:
export const methods = { foo }; export const call = key => methods[key]();→ 任何对methods的动态访问都会让整个对象保留
验证是否生效:看打包产物 + 启用 --analyze
别只信配置,要亲眼确认:
- Vite 用户加
build.rollupOptions.plugins.push(visualizer())(用rollup-plugin-visualizer) - Webpack 用户运行
npm run build -- --analyze,打开生成的 HTML 查看模块引用关系 - 手动检查 dist 中的 bundle:搜索未使用的函数名是否真的消失了;用
grep -r "function unusedFn" dist/快速验证 - 开启
optimization.usedExports: true(Webpack)或确保 Vite 使用build.minify: 'esbuild'(esbuild 对 Tree Shaking 更严格)


















