commonjs 不支持 tree shaking,因其 require 是运行时行为、导出结构不固定、无明确导出标识符绑定、模块引用关系不可靠,导致编译器无法静态分析未使用导出。

因为 CommonJS 的 require 是运行时行为,不具备静态可分析性。
require 路径可以动态计算
构建工具在编译阶段看不到实际加载哪个模块。例如:
const mod = require(condition ? './a' : './b')const name = 'utils'; const utils = require(`./${name}`)try { require('./feature') } catch {}
这些写法让模块依赖关系只能在执行时确定,编译器无法提前知道哪些导出被用到、哪些没被用到。
导出结构不固定,无法枚举引用项
CommonJS 允许任意方式设置导出:
exports.foo = () => {}module.exports = { [dynamicKey]: value }if (env === 'dev') module.exports.log = console.log
导出对象可能被条件逻辑覆盖、拼接或延迟赋值,工具无法安全判断某个方法是否“从未被引用”。
没有明确的导出标识符绑定
ES6 模块中每个 export 都有固定名称和位置,能被 AST 精确识别;而 CommonJS 中导出只是对一个对象的属性操作,没有语法级导出声明。比如:
-
const _ = require('lodash')→ 整个包被当作一个黑盒引入 - 即使只调用了
_.get,工具也无法推断_.map或_.filter是否未使用
模块间引用关系不可靠
CommonJS 支持循环引用、重赋值 module.exports、甚至运行时修改已导出对象,导致依赖图不稳定。构建工具无法建立可信的引用链来标记“未使用导出”。











