tree shaking 效率取决于模块导出方式、调用关系静态可分析性及副作用控制;四条路径:按需导出独立纯函数、拆分为自定义 hook、消除类中隐式状态与副作用、优先使用具名导出。

Tree Shaking 效率高低,不取决于“函数式 vs 类式”的表面写法,而取决于模块导出方式、调用关系是否静态可分析、以及是否产生副作用或隐式依赖。把类改成函数本身不会自动触发 Tree Shaking,关键在如何组织导出、拆分逻辑、消除运行时分支和状态耦合。
下面从实操角度给出四条直接提升 Tree Shaking 效果的重构路径:
明确按需导出独立纯函数
避免把多个功能打包进一个类或对象再整体导出。类一旦被 import,整个类定义(包括未使用的方法)大概率保留在打包结果中。
- ✅ 好做法:每个工具函数单独 export
// utils.js export function formatDate(date) { /* ... */ } export function validateEmail(email) { /* ... */ } export function debounce(fn, delay) { /* ... */ } - ❌ 避免:导出一个包含全部方法的 Validator 类
export class Validator { formatDate() { /* ... */ } validateEmail() { /* ... */ } // 即使只用了 validateEmail,整个类仍可能被打包 }
将组件逻辑拆为可独立导入的自定义 Hook
React 场景下,类组件(尤其是继承 BaseComponent 的)天然形成强耦合,难以被摇掉;而自定义 Hook 是函数,且通常只导出单一职责逻辑,Tree Shaking 更友好。
-
把生命周期、状态管理、副作用等抽成独立 Hook
// hooks/useFetch.js export function useFetch(url) { /* 返回 data/loading/error */ } // hooks/useForm.js export function useForm(initialValues) { /* 返回 values/handlers */ } -
组件内只 import 当前需要的 Hook,其余不引入
import { useFetch } from './hooks/useFetch.js'; // ← 只引入这个 // 不 import useForm 或 useAuth,它们就不会进包
消除类实例中的隐式状态与副作用
类的 constructor 或方法内部若修改外部变量、发起请求、监听事件、操作 DOM,这些副作用会让打包器不敢轻易移除该类——因为它“可能影响程序行为”。
- ✅ 改为显式输入 + 纯输出
// 纯函数,无副作用,易被 shake export function calculateTotal(items, taxRate) { return items.reduce((sum, item) => sum + item.price, 0) * (1 + taxRate); } - ❌ 避免在类里埋副作用
class CartManager { constructor() { this.items = []; fetch('/api/cart').then(/* 修改 this.items */); // ← 打包器无法判定是否必要 } }
使用顶层 export + 无默认导出(named exports 优先)
Webpack 和 Vite 的 Tree Shaking 依赖 ES 模块的静态结构。export default 在某些场景下会阻碍分析(尤其搭配动态 import 或条件导出时),而具名导出(named export)配合 import { fn } from 'x' 能让构建工具精准追踪引用链。
- ✅ 推荐写法
// api.js export const getUser = (id) => fetch(`/users/${id}`); export const updateUser = (user) => fetch('/users', { method: 'PUT', body: user }); - 组件也保持具名导出
// components/DataTable.js export function DataTable({ data }) { /* ... */ } export function TableHeader({ title }) { /* ... */ } - 使用时精确导入
import { DataTable } from './components/DataTable.js'; // ← 其他函数不会被打包
不复杂但容易忽略:真正决定 Tree Shaking 效果的,不是你用 class 还是 function,而是模块是否干净、导出是否粒度合适、调用是否静态可追溯。函数式结构只是更自然地导向这种干净性,而非魔法开关。











