CommonJS (CJS) 和原生 ECMAScript 模块 (ESM) 的区别与联系
JavaScript 模块化系统主要有两种标准:CommonJS (CJS) 和 ECMAScript 模块 (ESM)。以下是它们的详细对比和总结:
区别
1. 语法和定义方式
-
CJS
使用module.exports
导出模块内容,使用require()
导入模块。例如:// 导出 module.exports = { sayHello: function () { console.log('Hello!'); }, }; // 导入 const myModule = require('./myModule'); myModule.sayHello();
-
ESM
使用export
和import
关键字:// 导出 export function sayHello() { console.log('Hello!'); } // 导入 import { sayHello } from './myModule.js'; sayHello();
2. 执行时机
-
CJS
模块内容是同步加载的,require()
会在代码执行到该行时立即执行并返回模块内容。 -
ESM
模块的解析和加载是异步的。模块内容在实际需要时才会被执行,这有助于提高加载性能,特别是在浏览器环境中。
3. 模块解析和路径
- CJS
- 默认情况下,路径解析基于当前工作目录。
.js
扩展名是可选的。
- ESM
- 路径必须明确指出文件类型(如
.js
或.mjs
)。 - 在 Node.js 中,ESM 模块的解析规则可能与 CJS 不同,尤其是在处理相对路径和包时。
- 路径必须明确指出文件类型(如
4. 顶级作用域
-
CJS
模块内容被封装在一个函数闭包中,每个模块有自己的作用域。 -
ESM
模块运行在真正的顶级作用域中,这使得某些功能(如顶级await
)成为可能,而在 CJS 中不行。
联系
1. 互操作性
- 现代 Node.js 提供了机制(例如
--experimental-specifier-resolution=node
标志)以允许 CJS 和 ESM 模块互相导入导出,尽管这可能需要特定的配置。 - 工具(如 Babel、Webpack)可以将 ESM 代码转换为 CJS,或提供兼容性层。
2. 模块系统的演进
- ESM 是 ECMAScript 标准的一部分,是 JavaScript 模块系统的未来方向。
- CJS 最初为服务器端设计,而 ESM 同时考虑了浏览器和服务器的需求。
3. 生态系统
- 许多流行的库和框架已经开始提供 ESM 版本。
- 尽管如此,CJS 仍然在现有项目和旧版 Node.js 环境中广泛使用。
总结
- CJS 和 ESM 的显著区别体现在语法、执行机制、路径解析和作用域上。
- ESM 是未来的趋势,逐渐取代 CJS,但短期内 CJS 仍会广泛使用。
- 在现代 JavaScript 开发中,CJS 和 ESM 共存是常见现象,开发者可以根据项目需求选择适合的模块系统。