CommonJS和ES模块区别对比
CommonJS 和 ES Modules(ESM)是 JavaScript 的两大模块化规范,设计目标、运行机制和适用场景均有显著差异。以下是两者的核心区别对比:
一、设计目标与标准化
- CommonJS
- 为 Node.js 设计,解决服务器端模块化问题。
- 非 ECMA 标准,是 Node.js 的默认模块系统。
- ES Modules
- ECMAScript 官方标准(ES6 引入),统一浏览器和 Node.js 的模块化方案。
- 支持静态分析和编译优化(如 Tree Shaking)。
二、加载机制
- CommonJS
- 同步加载:
require()
会阻塞后续代码执行,直到模块加载完成。 - 运行时解析:模块依赖在代码执行时动态解析,支持条件加载(如
if
语句中)。 - 缓存机制:加载过的模块会缓存,多次
require
同一模块返回相同实例。
- 同步加载:
- ES Modules
- 异步加载:默认静态加载,但支持
import()
实现异步加载(返回 Promise)。 - 编译时解析:依赖关系在代码编译阶段确定,便于工具优化(如代码分割)。
- 无缓存:每次导入都会重新加载模块,需手动管理缓存。
- 异步加载:默认静态加载,但支持
三、语法与导出/导入方式
特性 | CommonJS | ES Modules |
---|---|---|
导出方式 | module.exports 或 exports.xxx | export (命名导出)、export default (默认导出) |
导入方式 | require() | import { ... } from '...' (静态导入)或 import() (动态导入) |
默认导出 | 无显式语法,通过 module.exports | export default 明确指定默认导出 |
动态导入 | 仅通过 require() | 支持 import() 函数实现懒加载 |
四、作用域与变量绑定
- CommonJS
- 导入的是模块导出的“值快照”,后续模块更新不影响已导入的值。
- 顶层
this
指向module.exports
,可通过this.xxx
导出变量。
- ES Modules
- 导入的是模块导出的“实时绑定”(live binding),若模块值更新,导入的变量会同步变化。
- 默认开启严格模式,顶层
this
为undefined
。
五、循环依赖处理
- CommonJS
- 模块加载时返回未完全初始化的对象,可能导致循环依赖时出现
undefined
值。 - 示例:
a.js
和b.js
互相require
,可能读取到对方的未完成状态。
- 模块加载时返回未完全初始化的对象,可能导致循环依赖时出现
- ES Modules
- 模块导出的是“引用”,循环依赖时保留最后一次赋值的值,更安全。
- 示例:
a.js
和b.js
互相import
,可正确获取对方的最终值。
六、浏览器与 Node.js 支持
- CommonJS
- 浏览器不支持,需通过打包工具(如 Webpack)转换。
- Node.js 原生支持,是服务器端主流方案。
- ES Modules
- 浏览器原生支持(需
<script type="module">
),支持直接加载 HTTP/HTTPS 资源。 - Node.js v14+ 开始支持,需
.mjs
扩展名或"type": "module"
配置。
- 浏览器原生支持(需
七、性能与优化
特性 | CommonJS | ES Modules |
---|---|---|
模块解析速度 | 较慢(运行时解析) | 较快(静态解析) |
并发加载 | 阻塞主线程 | 支持异步加载,可并发请求 |
Tree Shaking | 不支持(动态加载) | 支持静态分析,可剔除未使用代码 |
八、适用场景建议
- 优先选择 CommonJS:
- Node.js 传统项目或需要动态加载的场景(如插件系统)。
- 遗留代码迁移过渡期。
- 优先选择 ES Modules:
- 前端工程化项目(React/Vue 等)及现代构建工具链(Vite、Webpack 等)。
- 需要代码分割、懒加载或跨平台兼容性的场景。
九、混合使用与兼容
- Node.js 混合开发:
- 默认使用
.cjs
文件(CommonJS),.mjs
文件为 ESM。 - 通过动态
import()
加载 ESM 模块。
- 默认使用
- 浏览器 Polyfill:
- 使用
<script type="module">
直接加载 ESM 模块。 - 通过打包工具将 CommonJS 转换为 ESM。
- 使用
总结
CommonJS 和 ESM 的核心差异源于设计目标:前者面向服务器端同步场景,后者面向前端异步与性能优化。随着 Node.js 对 ESM 的支持完善(如 node --experimental-modules
),两者界限逐渐模糊,但 ESM 因其标准化和性能优势,成为现代开发的主流选择。