一、CommonJS 模块系统
CommonJS 是 Node.js 默认采用的模块系统,主要用于服务器端 JavaScript。
1. 导出方式
(1) 导出单个值
// 方式1:直接赋值给 module.exports
module.exports = function() {
console.log('Hello from CommonJS');
};
// 方式2:先定义后赋值
const myFunction = () => {
console.log('Hello from CommonJS');
};
module.exports = myFunction;
(2) 导出多个值
// 方式1:导出对象
module.exports = {
add: (a, b) => a + b,
subtract: (a, b) => a - b
};
// 方式2:逐个添加属性
exports.add = (a, b) => a + b;
exports.subtract = (a, b) => a - b;
// 注意:不能直接给 exports 赋值,这会断开与 module.exports 的链接
// 错误示例:exports = { add, subtract };
2. 导入方式
(1) 导入整个模块
const math = require('./math.js');
console.log(math.add(2, 3)); // 5
(2) 解构导入
const { add, subtract } = require('./math.js');
console.log(add(2, 3)); // 5
(3) 导入核心模块
const fs = require('fs');
const path = require('path');
(4) 导入 JSON 文件
const config = require('./config.json');
(5) 导入目录(自动查找 index.js)
const myModule = require('./my-module-dir');
二、ES Module (ESM) 模块系统
ES Module 是 ECMAScript 标准模块系统,现代浏览器和 Node.js 都支持。
1. 导出方式
(1) 命名导出 (Named Exports)
// 方式1:声明时直接导出
export const name = 'Alice';
export function greet() {
console.log('Hello from ESM');
}
// 方式2:先定义后统一导出
const age = 30;
const sayHello = () => console.log('Hi');
export { age, sayHello };
// 方式3:导出时重命名
export { age as userAge, sayHello as greet };
(2) 默认导出 (Default Export)
// 每个模块只能有一个默认导出
// 方式1:直接默认导出
export default function() {
console.log('Default export');
}
// 方式2:先定义后默认导出
const myComponent = () => console.log('My Component');
export default myComponent;
// 方式3:默认导出对象
export default {
version: '1.0',
author: 'Alice'
};
(3) 混合导出
export const version = '1.0';
export default function main() {
console.log('Main function');
}
2. 导入方式
(1) 导入命名导出
import { name, greet } from './module.js';
import { name as userName, greet as sayHello } from './module.js';
(2) 导入默认导出
import mainFunction from './module.js';
import myDefault from './module.js';
(3) 混合导入
import mainFunction, { version } from './module.js';
(4) 导入所有导出
import * as module from './module.js';
console.log(module.version);
module.default();
(5) 仅执行模块(不导入任何内容)
import './module.js'; // 适用于有副作用的模块
三、动态导入 (Dynamic Imports)
动态导入允许在运行时按需加载模块,返回 Promise。
1. CommonJS 动态导入
CommonJS 的 require 本身就是动态的,可以在任何地方使用:
// 条件加载
if (condition) {
const module = require('./moduleA');
} else {
const module = require('./moduleB');
}
// 动态路径
const path = someCondition ? './moduleA' : './moduleB';
const module = require(path);
2. ES Module 动态导入
ESM 使用 import() 函数实现动态导入:
(1) 基本用法
import('./module.js')
.then(module => {
module.default();
console.log(module.version);
})
.catch(err => {
console.error('模块加载失败', err);
});
(2) 结合 async/await
async function loadModule() {
try {
const module = await import('./module.js');
module.greet();
} catch (error) {
console.error('加载失败', error);
}
}
(3) 动态路径
const lang = getUserLanguage();
import(`./locales/${lang}.js`)
.then(module => {
// 使用本地化模块
});
(4) 多个模块并行加载
Promise.all([
import('./moduleA.js'),
import('./moduleB.js')
]).then(([moduleA, moduleB]) => {
// 使用两个模块
});
3. 动态导入的应用场景
- 代码分割:减少初始加载体积
- 按需加载:只在需要时加载模块
- 条件加载:根据不同环境/条件加载不同模块
- 路由级加载:SPA 中的路由懒加载
- Polyfill 加载:根据浏览器特性动态加载
四、CommonJS 与 ES Module 互操作
1. 在 CommonJS 中加载 ESM
Node.js 中必须使用动态 import():
// commonjs 文件
(async () => {
const esModule = await import('./es-module.mjs');
esModule.someFunction();
})();
2. 在 ESM 中加载 CommonJS
可以直接使用 import 语法:
// esm 文件
import cjsModule from './commonjs-module.cjs';
console.log(cjsModule.someValue);
// CommonJS 的 module.exports 会作为默认导出
import { namedExport } from './commonjs-module.cjs'; // 错误!不能这样解构
// 正确方式:先导入默认,再解构
import cjsModule from './commonjs-module.cjs';
const { namedExport } = cjsModule;
五、最佳实践建议
- 新项目:优先使用 ES Module
- Node.js 项目:
- 新项目设置
"type": "module" - 旧项目继续使用 CommonJS 或逐步迁移
- 新项目设置
- 浏览器项目:
- 使用打包工具处理模块
- 或直接使用
<script type="module">(现代浏览器)
- 动态导入:
- 性能敏感场景使用代码分割
- 注意错误处理
- 模块设计:
- 保持模块功能单一
- 合理使用默认导出和命名导出
- 避免复杂的循环依赖
六、关键区别总结
| 特性 | CommonJS | ES Module |
|---|---|---|
| 加载方式 | 同步 | 异步 |
| 语法 | require/module.exports | import/export |
| 动态导入 | 原生支持 | 使用 import() 函数 |
| 加载时机 | 运行时 | 编译时静态分析 |
| 值传递 | 值拷贝 | 值引用(实时绑定) |
顶层 this | 指向 module.exports | 指向 undefined |
| 主要环境 | Node.js | 浏览器和现代 Node.js |