由于 ES 模块化规范出来的比较晚,所以出现了现在 CommonJS 模块和 ES 模块共存的情况。因此在开发中可能会遇到 ES 模块引用 CommonJS 模块 或者 CommonJS 模块引用 ES 模块 的问题。
CommonJS 模块导入 ES 模块
在 node.js 22.0.0 版本之前,CommonJS 模块不能使用 require()
命令加载 ES 模块,但是可以使用动态导入命令( import()
)来导入 ES 模块。具体看下面的例子:
创建 ES 模块:
// lib/esm.js
export const a = 1;
export const b = 2;
export function foo () {
return 3;
}
由于 node.js 默认会把 .js
结尾的文件当做 CommonJS 模块来处理,所以需要一个带有 type 为 module
的 package.json
文件来告诉 node.js 将该文件当做 ES 模块来处理,或者将该文件后缀命名为 mjs
,node.js 也会将该文件作为 ES 模块处理。
这个例子使用前一种方式,使用带有 type 为 module
的 package.json
文件来告诉 node.js 将该文件当做 ES 模块来处理:
// lib/package.json
{
"type": "module"
}
在 cjs.js
文件中使用动态导入语法(import()
)导入 ES 模块:
// cjs.js
async function main() {
// 在 CommonJS 模块中引入 ES 模块
const mod = await import('./lib/esm.js');
const { a, b, foo } = mod;
console.log(a);
console.log(b);
console.log(foo());
}
main();
在终端使用 node 命令运行 cjs.js 的代码,可以发现代码正常输出了,说明 CommonJS 模块成功的引入了 ES 模块的代码:
不过随着 node.js 22.0.0 版本的发布,CommonJS 模块中也可以使用 require()
命令导入 ES 模块了。不过这个目前还是实验性的特性,因此在执行代码的时候需要加上 --experimental-require-module
标识。
改造上面例子中的 cjs.js
文件代码为如下内容:
// cjs.js
const esm = require('./lib/esm.js')
const { a, b, foo } = esm;
console.log(a);
console.log(b);
console.log(foo());
然后在终端运行 cjs.js 文件代码时,带上 --experimental-require-module
的标识,可以发现代码也正常输出了,说明在 node.js 22.0.0 版本中,可以使用 require()
命令导入 ES 模块:
要注意的是,require()
命令会以同步的方式加载 ES 模块,而动态导入语法(import()
)则是以异步的方式加载 ES 模块。
ES 模块导入 CommonJS 模块
由于 ES 模块化规范出来得比较晚,通常新的东西兼容旧的东西是比较容易的。
ES 模块可以通过 import
命令导入 CommonJS 模块,但是只能整体加载,不能像 ES 模块一样只加载单一的输出项。
因为 ES 模块会静态分析,而 CommonJS 模块的代码是无法进行静态分析的,因此只能整体加载。
下面来看在 ES 模块中导入 CommonJS 模块的例子
首先,创建 cjs.js 文件,代码如下
// lib/cjs.js
module.exports = {
a: 1,
b: 2,
foo: function () {
return 3
}
}
然后,使用带有 type 值为 commonjs
的 package.json 文件,告诉 node.js ,此 cjs.js 文件为 CommonJS 模块
// lib/package.json
{
"type": "commonjs"
}
创建 esm.js 文件,导入 cjs.js 文件
// esm.js
import pkg from './lib/cjs.js'
const { a, b, foo } = pkg;
console.log(a);
console.log(b);
console.log(foo());
由于 node.js 默认会把 .js
后缀的文件当做 CommonJS 模块处理,因此需要定义带有 type 值为 module
的 package.json 文件,告诉 node.js ,此 esm.js 文件为 ES 模块
// package.json
{
"type": "module"
}
这个例子的整体目录如下:
最后,在终端执行 esm.js 文件的代码
可以发现 ES 模块成功导入了 CommonJS 模块。
总结
由于 ES 模块化规范出来的比较晚,所以出现了现在 CommonJS 模块和 ES 模块共存的情况。因此在开发中可能会遇到 ES 模块与 CommonJS 模块互相引用的情况。
在 CommonJS 模块中,可以使用动态导入语法(import()
)导入 ES 模块,在 node.js 22.0.0 版本及之后,则支持了使用 require()
命令导入 ES 模块,当然到目前为止,这个还是实验性的特性。
在 ES 模块中,则可使用 import
命令导入 CommonJS 模块,由于 ES 模块会进行静态分析,而 CommonJS 模块是无法进行静态分析的,所以只能整体加载,无法单一的加载输出项。