Node.js ES Modules 与 CommonJS 对比
Node.js 支持两种模块系统:CommonJS(原生)和 ES Modules(ES6 标准)。
CommonJS 基本语法
JavaScript
// 导出 - math.js
module.exports.add = (a, b) => a + b;
module.exports.subtract = (a, b) => a - b;
// 或
module.exports = { add, subtract };
// 引入 - app.js
const math = require('./math');
const { add, subtract } = require('./math');
ES Modules 基本语法
JavaScript
// 导出 - math.mjs
export function add(a, b) {
return a + b;
}
export function subtract(a, b) {
return a - b;
}
// 或
export { add, subtract };
// 默认导出
export default { add, subtract };
// 引入 - app.mjs
import { add, subtract } from './math.mjs';
import math from './math.mjs'; // 默认导出
import * as math from './math.mjs'; // 全部导入
核心差异对比
| 特性 | CommonJS | ES Modules |
|---|---|---|
| 导出语法 | module.exports | export |
| 引入语法 | require() | import |
| 加载时机 | 运行时(动态) | 解析时(静态) |
| 文件扩展名 | .js, .cjs | .mjs, .js(需配置) |
| 值的复制 | 值复制 | 值引用(实时绑定) |
| this | module.exports | undefined |
| 顶层 await | 不支持 | 支持 |
值绑定差异
JavaScript
// CommonJS - 值复制
// counter.js
let count = 0;
module.exports = {
count,
increment: () => ++count
};
// main.js
const counter = require('./counter');
console.log(counter.count); // 0
counter.increment();
console.log(counter.count); // 0(未更新)
JavaScript
// ES Modules - 值引用(实时绑定)
// counter.mjs
export let count = 0;
export function increment() {
++count;
}
// main.mjs
import { count, increment } from './counter.mjs';
console.log(count); // 0
increment();
console.log(count); // 1(实时更新)
启用 ES Modules
JavaScript
// 方式1:使用 .mjs 扩展名
// app.mjs
import fs from 'fs';
// 方式2:package.json 配置
{
"type": "module"
}
// 然后使用 .js 扩展名即可
// 方式3:命令行指定
node --experimental-modules app.mjs
互操作性
在 ES Modules 中引入 CommonJS
JavaScript
// utils.cjs(CommonJS)
module.exports = { add: (a, b) => a + b };
// app.mjs(ES Module)
import utils from './utils.cjs'; // 作为默认导出
console.log(utils.add(1, 2));
// CommonJS 导出整体作为默认导出
// 不能直接 import { add } from './utils.cjs'
在 CommonJS 中引入 ES Modules
JavaScript
// math.mjs(ES Module)
export const add = (a, b) => a + b;
// app.cjs(CommonJS)
// 不能使用 require('./math.mjs')
// 需使用动态 import
const math = await import('./math.mjs');
console.log(math.add(1, 2));
循环依赖处理
JavaScript
// CommonJS:返回缓存副本
// a.js
const b = require('./b');
exports.a = 1;
// b.js
const a = require('./a'); // 得到未完成的 exports
// ES Modules:返回实时绑定
// a.mjs
import { b } from './b.mjs';
export let a = 1;
// b.mjs
import { a } from './a.mjs'; // 实时绑定
选择建议
| 场景 | 推荐 |
|---|---|
| Node.js 后端项目 | CommonJS(更兼容) |
| 前端/全栈项目 | ES Modules(标准化) |
| 需要动态加载 | CommonJS(require 可动态) |
| 需要静态分析 | ES Modules(import 可分析) |
| TypeScript 项目 | ES Modules |
要点总结
- CommonJS 使用 require/module.exports,运行时加载
- ES Modules 使用 import/export,静态解析
- ES Modules 导出值是实时绑定,CommonJS 是值复制
- CommonJS 可动态 require,ES Modules import 必须顶层
- Node.js 推荐使用 CommonJS,新项目可用 ES Modules
📝 发现内容有误?点击此处直接编辑