ES6 模块(ESM)和 CommonJS 是 JavaScript 中两种主要的模块规范,它们在设计理念、语法和行为上有显著区别。以下是两者的核心差异:
1. 语法规范与用途
-
•
ES6 模块(ESM)
是 ECMAScript 2015 标准化的模块系统,适用于浏览器和 Node.js(需显式启用),是 JavaScript 官方模块规范。
核心关键字:
export
、import
、export default
。 -
•
CommonJS
是 Node.js 原生的模块规范,主要为服务器端设计,早期用于解决 Node.js 中的模块依赖问题。
核心关键字:
module.exports
、exports
、require()
。
2. 导出 / 导入语法
操作 | ES6 模块(ESM) | CommonJS |
---|---|---|
默认导出 | export default { ... } |
module.exports = { ... } |
命名导出 | export const a = 1; export function f() {} |
exports.a = 1; exports.f = function() {} |
导入默认 | import obj from './module.js' |
const obj = require('./module.js') |
导入命名 | import { a, f } from './module.js' |
const { a, f } = require('./module.js') |
3. 加载时机与方式
-
•
ES6 模块:静态加载
-
•
导入和导出语句在代码解析阶段执行(编译时加载),而非运行时。
-
•
导入路径必须是静态字符串(不能动态拼接)。
-
•
支持
import()
动态导入(返回 Promise)。
示例:
javascript
运行
--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown// 静态导入(解析时执行) import { utils } from './tools.js'; // 动态导入(运行时执行) import('./tools.js').then(module => { ... });
-
-
•
CommonJS:动态加载
-
•
导入(
require()
)在代码运行阶段执行,支持动态路径。 -
•
模块加载是同步的(Node.js 中对本地模块有缓存优化)。
示例:
javascript
运行
--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown// 支持动态路径 const path = './tools.js'; const utils = require(path);
-
4. 模块输出的本质
-
•
ES6 模块:值的引用
-
•
导出的是值的只读引用,模块内部值变化时,导入方会同步更新。
-
•
导入的变量是只读的(不能重新赋值)。
示例:
javascript
运行
--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown// module.js export let count = 0; export function increment() { count++; } // main.js import { count, increment } from './module.js'; increment(); console.log(count); // 输出 1(跟随模块内部变化)
-
-
•
CommonJS:值的拷贝
-
•
导出的是值的拷贝(对象导出的是引用地址的拷贝),模块内部值变化不影响导入方。
-
•
导入的变量可以重新赋值。
示例:
javascript
运行
--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown// module.js let count = 0; module.exports = { count, increment: () => { count++; } }; // main.js const { count, increment } = require('./module.js'); increment(); console.log(count); // 输出 0(拷贝的值未更新)
-
5. 循环依赖处理
-
•
ES6 模块
由于静态解析,模块在加载时会先创建「模块记录」,循环依赖时能正确引用未完全初始化的模块(返回部分导出值)。
-
•
CommonJS
循环依赖时,会返回模块当前已执行部分的导出值(可能是不完整的),依赖后续执行补充。
6. 顶层 this****指向
-
•
ES6 模块:顶层
this
为undefined
。 -
•
CommonJS:顶层
this
指向当前模块的module.exports
。
7. 使用场景
-
•
ES6 模块
-
•
浏览器环境(原生支持,需在
<script type="module">
中使用)。 -
•
现代前端工程(Vue、React 项目,通过 Webpack/Vite 打包)。
-
•
Node.js 13.2+(需在
package.json
中设置"type": "module"
)。
-
-
•
CommonJS
-
•
Node.js 环境(默认模块系统)。
-
•
早期 Node.js 生态的库(如 Express 中间件)。
-
总结
特性 | ES6 模块(ESM) | CommonJS |
---|---|---|
加载时机 | 解析时(静态) | 运行时(动态) |
输出本质 | 值的引用(只读) | 值的拷贝(可修改) |
循环依赖处理 | 基于模块记录(更可靠) | 基于执行状态(可能不完整) |
适用环境 | 浏览器 + 现代 Node.js | Node.js 原生 |
动态导入支持 | import() (返回 Promise) |
require() 天然支持 |
现代前端开发以 ES6 模块为主,而 CommonJS 主要用于 Node.js 后端开发。在实际项目中,两者可通过打包工具(如 Webpack)相互兼容。