在 Node.js 中,模块系统遵循 CommonJS 规范(默认)和 ES Modules(需配置)。以下是两种方式的详细说明:
一、CommonJS 模块系统(Node.js 默认)
ⅰ. 导出模块
1. 导出单个值:
// math.js
const add = (a, b) => a + b;
module.exports = add; // 导出单个函数
// 或导出对象
module.exports = { add };
2. 导出多个值:
// utils.js
const greet = name => `Hello, ${name}!`;
const PI = 3.14159;
// 方式1:逐个挂载到 exports 对象
exports.greet = greet;
exports.PI = PI;
// 方式2:整体导出对象
module.exports = { greet, PI };
ⅱ. 导入模块
// app.js
const add = require('./math.js'); // 导入单个函数
const { greet, PI } = require('./utils.js'); // 解构导入
console.log(add(2, 3)); // 5
console.log(greet('Alice')); // "Hello, Alice!"
二、ES Modules(ESM,需配置)
ⅰ. 启用 ESM
- 方式1:在
package.json
中添加:
{
"type": "module" // 所有 .js 文件视为 ESM
}
- 方式2:文件使用
.mjs
后缀(无需配置)。
ⅱ. 导出模块
// math.mjs
export const add = (a, b) => a + b;
export const PI = 3.14159;
// 或默认导出
export default function multiply(a, b) {
return a * b;
}
ⅲ. 导入模块
// app.mjs
import { add, PI } from './math.mjs'; // 命名导入
import multiply from './math.mjs'; // 默认导入
console.log(add(2, 3)); // 5
console.log(multiply(2, 3)); // 6
三、混合使用 CommonJS 和 ESM
ⅰ. 在 ESM 中导入 CommonJS 模块
// esm-app.mjs
import cjsModule from './commonjs-module.js'; // CommonJS 模块会被自动转换
console.log(cjsModule.greet('Bob'));
ⅱ. 在 CommonJS 中导入 ESM 模块(需动态导入)
// commonjs-app.js
(async () => {
const esmModule = await import('./esm-module.mjs');
console.log(esmModule.add(2, 3));
})();
四、核心注意事项
- 路径规则:
-
require('./module')
:导入当前目录下的module.js
或module/index.js
。require('module')
:导入node_modules
中的核心模块或第三方模块。
- 循环依赖:
-
- 避免模块 A 依赖 B,同时 B 又依赖 A,可能导致未初始化的导出。
- 缓存机制:
-
- 模块首次加载后会被缓存,后续
require()
直接返回缓存结果。
- 模块首次加载后会被缓存,后续
- 文件扩展名:
-
- CommonJS:可省略
.js
(如require('./math')
)。 - ESM:必须完整指定路径(如
import './math.mjs'
)。
- CommonJS:可省略
五、代码示例对比
操作 | CommonJS | ES Modules |
默认导出 |
|
|
命名导出 |
|
|
导入默认导出 |
|
|
导入命名导出 |
|
|
六、最佳实践建议
- 新项目:优先使用 ES Modules(现代浏览器和 Node.js 均原生支持)。
- 旧项目迁移:逐步替换
require
为import
,可通过工具(如esm
包)过渡。 - 第三方库:检查是否支持 ESM(如 Lodash 需
lodash-es
版本)。
通过合理选择模块系统,可以更好地组织代码结构并兼容不同运行环境。