Tree Shaking 依赖 ES 模块静态结构,需用具名/默认导出、避免聚合导出与顶层副作用,配合 package.json sideEffects 声明、exports 字段分发及模块拆分。

导出方式必须是 ES 模块静态结构
Tree Shaking 依赖于静态分析,只有 ES 模块的具名导出(named export)和默认导出(default export) 能被正确识别。避免使用 export * from 或动态导出(如通过对象拼接再整体导出),这些会阻断依赖图追踪。
推荐写法:
- 每个功能单独导出:
export const debounce = ...、export class EventEmitter { ... } - 避免聚合导出文件中做运行时判断:❌ 不要这样:
const utils = { debounce, throttle }; export default utils; - 若需统一入口,用
export { debounce, throttle } from './debounce.js';显式重导出,而非对象转发
避免副作用导致模块无法被剔除
即使某个模块没被引用,只要它有**顶层副作用**(如执行函数、修改全局、发起请求、操作 DOM),打包工具会保守保留整个模块。这对库作者尤其关键。
做法包括:
立即学习“Java免费学习笔记(深入)”;
- 把副作用逻辑下沉到函数内部,而非模块顶层:✅ 正确 —— 导出函数,调用时才执行副作用;❌ 错误 —— 模块加载即
console.log('init')或document.body.classList.add('lib-ready') - 在
package.json中显式声明:"sideEffects": false(表示全无副作用),或列出有副作用的文件路径(如["*.css", "src/polyfill.js"]) - 避免在导出前执行非纯初始化代码;必要时用
/*#__PURE__*/注释标记可安全移除的调用
类型与文档不干扰实际导出结构
TypeScript 类型、JSDoc 注释、内联文档字符串等,本身不影响 Tree Shaking,但若它们诱导出“虚假导出”就可能破坏效果。
注意点:
- 不要为类型兼容而添加无用的导出,例如:
export type { Foo };是安全的,但export { Foo as FooType };(且Foo是值)会造成冗余导出项 - 生成 d.ts 时确保
declaration: true且importsNotUsedAsValues: "error"(TS 4.5+),防止类型被误当值导出 - 避免在导出语句中混用类型与值:
export { foo, type Bar }是 OK 的,但不要写export { foo, Bar }且Bar实际只是类型别名
构建输出保持 ESM + 纯净 CommonJS 分离
现代打包器(如 Vite、Webpack 5+)优先识别 exports 字段中的 import 入口。一个高性能库应提供明确的模块格式分发。
配置建议:
- 在
package.json中使用"exports"字段精确控制:
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
},
"./debounce": {
"import": "./dist/debounce.mjs",
"require": "./dist/debounce.cjs"
}
}
- 确保
.mjs文件是真正 ES 模块(不含require、module.exports),且不带 Babel 转译的__esModule标记 - 避免将所有功能打包进单个
index.js;按功能拆分为独立模块文件(如throttle.js,deepClone.js),便于细粒度摇树



















