学自:https://webreflection.medium.com/cjs-vs-esm-5f8b90a4511a
安全性
ESM 较 CJS 更加安全。
// module.js
export let a = 'a';
export let b = 'b';
export let c = 'c';
// index.js
import * as esmModule from './module.js';
esmModule.a = 'z';
console.log(esmModule);
// result:
esmModule.a = 'z';
^
TypeError: Cannot assign to read only property 'a' of object '[object Module]'
作为对比,CJS 可以修改 modules.export 中的值。
测试
CJS 相较于 ESM 更容易测试。
- CJS 支持 缓存无效
- 分析工具可以挂载在任何模块上
- 代码执行时,可以分析第三方组件的指标和变化
require('module');
// some stuff
// module cache invalidation
delete require.cache[require.resolve('module')];
// some other stuff
require('module');
Treeshaking
ECMAScript 可以支持 Treeshaking,CommonJS 不行。ECMAScript 现实中也没能从 Treeshaking 中获利。因为浏览器需要下载对应的资源(node 需要阅读对应的代码)才能知道如何 Treeshaking。事实上,应该只有 AOT (Ahead of Time) 编译器可以从 Treeshaking 获利。
幸运的是,在语法分析阶段(parsing phase),执行引擎可以知道哪些代码需要处理和优化,哪些不能。但是这引起了一些误解。
- CJS 和 ESM 都支持动态引入代码,动态引入代码事实上打败了 treeshaking 的优势。
async function loadMyModule() {
const {
default: defaultImport,
namedExport1,
namedExport2
} = await import('./mixedExportModule.js');
// ...
}
loadMyModule();
- CJS / ESM 都支持对某些模块有特定的 export 路径。例如,lo-dash 可以把所有的模块在一个文件中 export,也可以用单独的文件 export 每个模块。
可见 CJS / ESM 在 Treeshaking 方面可能都不是赢家。如果一个模块从 Treeshaking 中获利很多,可能有更好的方案,把这个模块切分成多个模块或者可以单独引入的子模块。
live binding + default
ESM 支持 live binding,而 CommonJS 不支持。
ESM 在 export 变量时,是以引用的方式被引入的。这与 CommonJS 的方式有所不同。当模块内的变量值有所变动时,引入方再读取变量值时可以获得新的值。
MIME 类型
只要 HTTP 中相关的 header mime 类型是对的,ESM 不关心文件后缀名。例如 .js
在 NodeJS 世界上是一个 CJS 类型文件。 在Web 中 .gif
的文件,也可以用 application/javascript
类型返回。
结论
谈论一下技术细节可能比简单说 ESM 和 CJS 哪个更好有意义。