高效Tree Shaking的关键在于代码符合ES Module静态分析规则:①用命名导出而非默认导出对象;②模块顶层不执行副作用;③精准导入而非整体引入;④构建链保持ESM结构,禁用Babel模块转换、TS设module为esnext。

要让打包工具(如 Webpack、Vite、Rollup)更高效地执行 Tree Shaking,关键不在于工具配置本身,而在于你写的代码是否“可被静态分析”——也就是是否符合 ES Module 的确定性规则。代码规范直接决定摇树能否发生、能摇掉多少。
用命名导出,避免默认导出对象
命名导出(export const foo = ... 或 export function bar())让工具能精确识别每个导出项;而默认导出一个对象(export default { foo, bar })会让整个对象被视为“已使用”,即使只 import 了其中一项,bar 也无法被摇掉。
- ✅ 推荐写法:export const add = (a, b) => a + b; export const multiply = (a, b) => a * b;,再 import { add } from './math'
- ❌ 避免写法:export default { add, multiply };,再 import utils from './math'; utils.add(...)
不把模块顶层变成“副作用现场”
如果模块在顶层就执行逻辑(比如调用 API、修改 window、引入样式、触发 console),打包工具会认为它有副作用,为安全起见保留整个文件——哪怕你只 import 了里面一个函数。
- ✅ 安全写法:模块只做声明,不执行任何有外部影响的操作
- ❌ 危险写法:console.log('init');、fetch('/api/init');、import './style.css';(除非明确声明为副作用)
- 若必须保留副作用,应在 package.json 中设置 "sideEffects": ["*.css", "index.js"],而非 true
导入时精准引用,不“兜底式”引入
Tree Shaking 不是万能的,它无法从一个被整体引用的对象里智能拆解出未使用的属性。你的 import 写法决定了工具有没有机会摇。
- ✅ 正确:import { debounce, throttle } from 'lodash-es';
- ❌ 无效:import _ from 'lodash-es'; _.debounce(...); —— 整个 _ 对象被标记为已使用
- 组件库同理:import Button from '@ant-design/react-button'; 比 import { Button } from '@ant-design/components'; 更可靠(前提是子路径存在且导出是具名的)
确保构建链不破坏 ESM 结构
即使你写了标准的 import/export,Babel 或 TypeScript 若把它们转成 require,Tree Shaking 就彻底失效。
- Babel 配置中必须设 modules: false(禁用模块转换)
- TypeScript 的 tsconfig.json 中应设 "module": "esnext" 或 "preserve"
- 检查最终输出的 bundle 是否还含 require 或 exports —— 出现即说明 ESM 被破坏


















