ES模块化是tree-shaking前提,需用静态import/export;命名导出优于默认导出;需避免副作用或显式声明;第三方库须支持ESM且合理导出。

JavaScript模块化开发是实现高效 tree-shaking 的前提条件。只有使用 ES 模块(import/export)语法,构建工具(如 Webpack、Rollup、Vite)才能静态分析依赖关系,识别出未被引用的导出内容并将其剔除。CommonJS(require/module.exports)因动态特性无法在编译期确定导出使用情况,基本不支持 tree-shaking。
ES 模块语法是 tree-shaking 的基础
ES 模块的导入导出必须是顶层的、静态的声明,不能出现在条件语句或函数中。这使得打包器能在不执行代码的前提下,准确追踪哪些 export 被哪些 import 引用。
- ✅ 支持:
export const foo = 1;、export function bar() {}、import { foo } from './utils.js'; - ❌ 不支持(破坏静态性):
if (cond) export const baz = 2;、export default () => {}(若被默认导入且未调用,部分工具可能保留)
命名导出比默认导出更利于精准剔除
当模块提供多个功能时,使用命名导出(export { a, b, c })能让工具明确知道每个导出项是否被使用。而默认导出(export default { a, b, c })会把整个对象视为一个不可拆分单元,即使只用其中一项,整个对象也可能被保留。
例如:
- ✅ 命名导出 → 可单独摇掉:
export const add = () => {};+export const sub = () => {};,仅import { add } from './math'时,sub可被移除 - ⚠️ 默认导出对象 → 难以摇掉:
export default { add: () => {}, sub: () => {} };,即使只取add,整个对象仍可能保留在包中
避免副作用干扰摇树判断
如果模块顶层有执行逻辑(如直接调用函数、修改全局变量、发送请求),会被视为“有副作用”,即使没有被显式导入,也可能被保留。可通过 /*#__PURE__*/ 注释或 package.json 中的 "sideEffects": false 显式声明无副作用。
- ❌ 有副作用示例:
console.log('init');、localStorage.setItem('flag', '1');(未标记时可能阻止摇树) - ✅ 安全写法:
/*#__PURE__*/ console.log('debug');(告诉工具该调用可安全移除) - ✅ 项目级配置:
"sideEffects": ["*.css", "*.scss"]表示仅这些文件有副作用,其余 JS 文件可放心摇树
第三方库需支持 ESM 且导出方式合理
即便你用 ES 模块,若所依赖的库发布的是 CommonJS 版本,或其 ESM 版本内部大量使用默认导出/动态逻辑,tree-shaking 效果也会大打折扣。优先选择原生支持 ESM、采用命名导出、并正确标注 sideEffects 的库(如 Lodash-es 而非 Lodash)。
- 推荐:
import { debounce } from 'lodash-es';(可摇) - 避免:
import _ from 'lodash';(CommonJS 全量引入,不可摇) - 验证方法:查看库的
package.json是否有"type": "module"或"exports"字段指向 ESM 入口
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











