Tree Shaking 对类的普通方法本身不失效,真正导致保留的是使用方式(如动态访问、instanceof)、副作用(如构造函数读 localStorage)及导出结构(如默认导出),而纯函数通过命名导出可实现精准剔除。

Tree Shaking 对类的普通方法(即定义在 class 内部的实例方法)本身并不直接“失效”,真正导致它们难以被剔除的,是类的使用方式、副作用干扰和导出结构问题,而非方法语法本身。关键在于:打包工具能否静态确认该方法从未被调用且无运行时影响。
类的方法为什么常被保留?
-
类定义本身不触发副作用,但类的使用常伴随隐式依赖
比如你写了:// utils.js export class NumberUtils { static clamp(value, min, max) { return Math.min(Math.max(value, min), max); } formatCurrency(n) { return `$${n.toFixed(2)}`; } }看似干净,但如果在别处写
new NumberUtils().formatCurrency(123),或更隐蔽地——通过instanceof、Object.getPrototypeOf、装饰器、反射(如Reflect.getMetadata)等方式间接访问,打包工具就无法安全断定formatCurrency是死代码。 -
默认导出类 + 解构/动态访问会切断静态分析链
export default class Utils { ... } // ❌ 默认导出类 // 使用方: import Utils from './utils'; const u = new Utils(); u.formatCurrency(); // 工具能识别 → 可摇 // 但若: const method = 'formatCurrency'; u[method](); // ⚠️ 动态属性访问 → 工具保守保留全部方法 -
类中存在静态块、getter/setter 或顶层执行逻辑
export class ApiClient { constructor() { this.token = localStorage.getItem('token'); } // 读 localStorage → 副作用 static init() { console.log('API ready'); } // 静态方法未调用,但类被引入就执行了构造函数 }只要
ApiClient被import,整个类体就可能被执行,工具不敢删任何部分。
怎么用纯函数代替?实操建议
核心原则:把行为从“对象能力”转为“独立可识别的导出单元”,让每个函数具备清晰的输入输出、无隐式状态、无顶层副作用。
✅ 正确做法:拆成命名导出的纯函数
// utils/number.js
export function clamp(value, min, max) {
return Math.min(Math.max(value, min), max);
}
export function formatCurrency(n) {
return `$${n.toFixed(2)}`;
}
// 使用方(精准引用)
import { clamp, formatCurrency } from './utils/number';
const safe = clamp(userInput, 0, 100);
const display = formatCurrency(total);- ✅ Tree Shaking 可精确标记
clamp是否被用,没用就整段函数体被剔除 - ✅ 不依赖
new、不触发构造逻辑、无this上下文污染 - ✅ 支持内联、压缩、甚至 Terser 的
/*#__PURE__*/标记进一步优化
✅ 进阶:组合与命名空间(保持可读性)
如果担心函数太多难管理,可用对象组织,但仍坚持具名导出:
// utils/number.js
export const clamp = (value, min, max) => Math.min(Math.max(value, min), max);
export const formatCurrency = (n) => `$${n.toFixed(2)}`;
export const isPositive = (n) => n > 0;
// 不要这样:
// export const number = { clamp, formatCurrency }; // ❌ 导出对象 → 摇树粒度变粗✅ 补充:配合构建配置确保生效
-
package.json中声明"sideEffects": false(适用于纯函数模块) - 确保 Babel 不转译 ESM:
modules: false - TypeScript 设置
"module": "ESNext",禁用isolatedModules: false - Webpack/Vite 使用生产模式构建(tree shaking 默认启用)
验证是否真的被摇掉?
- 在函数里加一句
console.log('never called')或debugger - 构建后搜索
dist/bundle.js,看该字符串是否存在 - 或用
webpack-bundle-analyzer查看模块内容明细
不复杂但容易忽略


















