CJS and ESM 优缺点以及应用

113 阅读2分钟

§ CJS 和 ESM的区别

CJS ==> 全称CommonJS

ESM ==> 全称ECMAScript Modules

1.语法:

CJS: 使用 require() 来导入模块,使用 module.exportsexports 来导出

ESM: 使用 importexport 关键字

2.运行时与编译时:

CJS: 在运行时解析依赖

ESM: 在编译时解析依赖

3.异步与同步:

CJS: 同步加载模块

ESM: 支持异步加载

4.作用域:

CJS: 每个模块都有自己的作用域

ESM: import 和 export 必须位于模块作用域

5.互操作性:

CJS: 在Nodejs中广泛使用,但原生浏览器不支持

ESM: 既可以在浏览器中使用,也可以在nodejs中使用

§ CJS 和 ESM 分别是怎么解决循环依赖的

循环依赖(或称为循环引用) 是一个在模块系统中常见的问题,不同的模块系统有不同的方式来处理这个问题

CJS:

在 CommonJS 中,当发生循环依赖时,模块系统会返回到目前为止已经解析(并执行)的部分。这意味着,在循环依赖的情况下,你可能得到一个不完全初始化的模块

假设有两个模块 A 和 B,它们相互依赖:

// A.js
const B = require('./B');
exports.name = 'Module A';

// B.js
const A = require('./A');
exports.name = 'Module B';

在这种情况下,当你尝试 require('./A') 或 require('./B'),模块系统会尝试解析两者,但由于循环依赖,它会返回一个不完全初始化的模块

ESM:

ESM 采用了一种不同的方法来处理循环依赖。由于 ESM 在编译时解析依赖,它能更好地处理这种情况。在 ESM 中,导入的值是只读引用,而不是值的拷贝。这意味着,即使存在循环依赖,你也会得到预期的结果

// A.js
import { name as BName } from './B.js';
export const name = 'Module A';

// B.js
import { name as AName } from './A.js';
export const name = 'Module B';

在这个例子中,由于 ESM 的静态解析特性,循环依赖会被正确地解析,而不会导致不完全初始化的模块

总结:

CJS: 在运行时解析模块,可能导致不完全初始化的模块。

ESM: 在编译时解析模块,能更好地处理循环依赖。