Node.js 模块系统:CommonJS vs ES Modules
理解两种模块系统的区别和使用场景
概述
Node.js 支持两种模块系统:
| 特性 | CommonJS | ES Modules |
|---|---|---|
| 关键字 | require / module.exports |
import / export |
| 加载方式 | 运行时同步加载 | 编译时静态加载 |
| 文件扩展名 | .js |
.mjs 或 .cjs |
| Node.js 支持 | 默认支持 | v13.2+ 稳定支持 |
CommonJS (CJS)
导出
javascript
// math.js
// 方式1:导出单个值
module.exports = function add(a, b) {
return a + b;
};
// 方式2:导出多个属性
module.exports.add = function(a, b) { return a + b; };
module.exports.subtract = function(a, b) { return a - b; };
// 方式3:使用 exports(注意:不能直接赋值)
exports.PI = 3.14159;
exports.multiply = function(a, b) { return a * b; };
导入
javascript
// app.js
// 导入整个模块
const math = require('./math');
console.log(math.add(1, 2));
// 解构导入
const { add, PI } = require('./math');
console.log(PI);
// 导入内置模块
const fs = require('fs');
const path = require('path');
// 导入第三方模块
const express = require('express');
package.json 配置
json
{
"type": "commonjs" // 默认,可省略
}
ES Modules (ESM)
导出
javascript
// math.js 或 math.mjs
// 命名导出
export const PI = 3.14159;
export function add(a, b) {
return a + b;
}
export function subtract(a, b) {
return a - b;
}
// 默认导出
export default function(a, b) {
return a + b;
}
// 或者集中导出
const PI = 3.14159;
const add = (a, b) => a + b;
export { PI, add };
导入
javascript
// app.js 或 app.mjs
// 默认导入
import add from './math.js';
// 命名导入
import { add, subtract, PI } from './math.js';
// 重命名导入
import { add as sum } from './math.js';
// 导入所有
import * as math from './math.js';
// 副作用导入(只执行,不导入)
import './polyfill.js';
package.json 配置
json
{
"type": "module"
}
或使用 .mjs 扩展名(自动启用 ESM)
主要区别
1. 加载时机
javascript
// CommonJS - 运行时加载
const math = require('./math'); // 同步执行
// ES Modules - 编译时加载
import { add } from './math'; // 静态分析
2. 导出方式
javascript
// CommonJS - 值拷贝
let count = 0;
module.exports = { count };
count = 1; // 导出值不变,仍是 0
// ES Modules - 动态引用
export let count = 0;
count = 1; // 导出值同步更新为 1
3. this 指向
javascript
// CommonJS - this 指向 module.exports
console.log(this === module.exports); // true
// ES Modules - this 是 undefined
console.log(this); // undefined
实战示例
CommonJS 项目结构
project/
├── package.json (默认 commonjs)
├── utils/
│ └── math.js
└── app.js
javascript
// utils/math.js
module.exports.add = (a, b) => a + b;
// app.js
const { add } = require('./utils/math');
console.log(add(1, 2));
ES Modules 项目结构
project/
├── package.json ("type": "module")
├── utils/
│ └── math.js
└── app.js
javascript
// utils/math.js
export const add = (a, b) => a + b;
// app.js
import { add } from './utils/math.js';
console.log(add(1, 2));
混用技巧
在 CJS 中使用 ESM
javascript
// 需要 dynamic import
async function loadModule() {
const { add } = await import('./math.mjs');
return add(1, 2);
}
在 ESM 中使用 CJS
javascript
// 可以直接导入
import express from 'express';
import { readFile } from 'fs';
选择建议
使用 CommonJS 当:
- 需要动态加载模块
- 兼容旧项目或工具
- 编写简单的脚本工具
使用 ES Modules 当:
- 开始新项目
- 使用现代前端框架(React、Vue)
- 需要_tree-shaking_ 优化
- 编写可同时在浏览器和 Node.js 运行的代码
快速参考
javascript
// CommonJS
module.exports = { value: 1 };
const { value } = require('./file');
// ES Modules
export const value = 1;
import { value } from './file.js';
总结
- CommonJS:Node.js 传统方式,成熟稳定
- ES Modules:现代标准,未来趋势