tree shaking 在混用 commonjs 时会失效,因其运行时动态加载特性导致工具无法静态分析导出内容;esm 的 import 语法明确依赖,而 commonjs 的 require 和动态属性访问使推断不可行。

Tree Shaking 在混用 CommonJS(require/module.exports)时会直接失效,因为 CommonJS 是运行时动态加载,无法静态分析导出内容。Rollup、Webpack 等工具遇到 require 就会“放弃推断”,整块模块被保留——哪怕你只用了其中一行函数。
为什么 CommonJS 会让摇树停摆
ESM 的 import { foo } from './x.js' 在语法层面就明确了依赖项,构建工具能逐层追踪;而 CommonJS 的 const mod = require('./x') 后可能通过 mod[condition ? 'a' : 'b']() 动态调用,工具无法在不执行代码的前提下判断哪些导出实际被用到。
一旦项目或依赖中存在以下任一情况,Tree Shaking 就会降级或完全跳过该模块:
- 源码里写了
require('lodash')或module.exports = {...} - 第三方库发布的是 CommonJS 格式(如老版本
lodash),且未提供 ESM 入口 - Babel 错误启用了
@babel/plugin-transform-modules-commonjs,把本该保留的export转成了module.exports
如何让 CommonJS 模块也能参与摇树
不能直接“摇”CommonJS,但可通过转换+约束,让它尽量接近可分析状态:
- 用
@rollup/plugin-commonjs(Rollup)或webpack.config.js中启用resolve: { fullySpecified: true }+experiments: { topLevelAwait: true },将 CommonJS 模块转为 ESM 形式再分析 - 优先切换到 ESM 友好替代品:比如用
lodash-es替代lodash,它原生支持命名导入,且package.json中已声明"sideEffects": false - 对必须保留的 CommonJS 依赖,在配置中显式排除摇树干扰:
Rollup 中可加external: ['lodash'],避免尝试分析;Webpack 中可在optimization.usedExports下配合module.rules设置parser: { require: false }来限制解析范围
绕过 CommonJS 的实用写法
不改依赖,也能减少体积浪费:
- 避免
import _ from 'lodash',改用具名导入:import { debounce, throttle } from 'lodash-es' - 若只能用 CommonJS 库,手动解构引入(需确认库支持):
const { cloneDeep } = require('lodash')—— 部分打包器(如 Webpack 5+)能识别这种写法并做有限优化 - 用动态
import()加载非首屏用的 CommonJS 模块,至少实现代码分割,不让它挤占主包
验证是否真被“摇”掉了
别信配置,看结果:
- 构建后打开 bundle 文件,搜索你确定没用的函数名(如
_.zipWith),确认不存在 - 用
rollup-plugin-visualizer或source-map-explorer查看模块引用图,观察 CommonJS 依赖是否仍以整包形式挂载 - 对比开启/关闭
@rollup/plugin-commonjs前后的包体积变化,判断转换是否真正带来收益
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











