JavaScript 模块导入:CommonJS 与 ES6 模块
JavaScript 的模块系统用于将代码拆分成独立、可复用的文件。主流有两种规范:CommonJS(主要用于 Node.js)和 ES6 模块(现代浏览器和新版 Node.js 支持)。它们在语法、加载方式和使用场景上有本质区别。
区分两种模块的核心差异
| 特性 | CommonJS | ES6 模块 |
|---|---|---|
| 加载时机 | 运行时加载 | 编译时加载(静态分析) |
| 导出方式 | module.exports 或 exports |
export |
| 导入方式 | require() |
import |
| 是否支持异步导入 | 否(同步) | 是(支持 import() 动态导入) |
| 默认是否严格模式 | 否 | 是 |
| 能否在浏览器直接使用 | 否(需打包工具) | 是(需 <script type="module">) |
在 Node.js 中使用 CommonJS
Node.js 默认使用 CommonJS 规范。按以下步骤操作:
- 创建一个名为
math.js的文件,写入导出逻辑:// math.js function add(a, b) { return a + b; }
function multiply(a, b) {
return a * b;
}
// 导出多个函数
module.exports = {
add,
multiply
};
2. **创建**另一个文件 `app.js`,**导入并使用**上面的模块:
```javascript
// app.js
const math = require('./math');
console.log(math.add(2, 3)); // 输出 5
console.log(math.multiply(4, 5)); // 输出 20
- 运行命令:
node app.js
注意:
require()的路径必须包含./或../表示相对路径,否则 Node.js 会去node_modules查找。
在浏览器或现代环境中使用 ES6 模块
ES6 模块是 JavaScript 官方标准,适用于现代浏览器和启用 ES 模块的 Node.js 环境。
在浏览器中使用
- 创建
utils.js文件:// utils.js export function formatDate(date) { return date.toISOString().split('T')[0]; }
export const PI = 3.14159;
2. **创建** HTML 文件,**引入**模块:
```html
<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
<title>ES6 Module Demo</title>
</head>
<body>
<script type="module">
import { formatDate, PI } from './utils.js';
console.log(formatDate(new Date())); // 如 "2024-06-15"
console.log(PI); // 3.14159
</script>
</body>
</html>
- 通过 HTTP 服务器打开该 HTML 文件(不能直接双击本地文件,因浏览器禁止
file://协议加载模块)。可使用以下任一命令启动简易服务器:# 如果安装了 Python 3 python -m http.server 8000
或使用 Node.js 的 http-server(需先 npm install -g http-server)
http-server
### 在 Node.js 中启用 ES6 模块
默认 `.js` 文件被视为 CommonJS。要使用 ES6 模块,有两种方式:
#### 方式一:文件扩展名为 `.mjs`
1. **重命名**文件为 `app.mjs`
2. **写入** ES6 模块代码:
```javascript
// app.mjs
import { PI } from './constants.js';
console.log(PI);
- 运行:
node app.mjs
方式二:在 package.json 中声明类型
- 在项目根目录创建或修改
package.json,添加字段:{ "type": "module" } - 此时所有
.js文件默认为 ES6 模块,可直接使用import/export:// main.js import { greet } from './greet.js'; greet();
导入导出的详细语法对照
CommonJS 导出方式
-
导出整个对象:
module.exports = { name: 'Alice', age: 30 }; -
逐个添加属性(等价于上例):
exports.name = 'Alice'; exports.age = 30; -
导出单个值(如类或函数):
module.exports = class User { constructor(name) { this.name = name; } };
CommonJS 导入方式
-
导入整个模块:
const userModule = require('./user'); -
解构导入(仅适用于导出为对象的情况):
const { name, age } = require('./user');
ES6 模块导出方式
-
命名导出(可多个):
export const API_URL = 'https://api.example.com'; export function fetchUser(id) { /* ... */ } -
默认导出(每个文件只能有一个):
export default class ApiService { /* ... */ } -
混合导出:
const version = '1.0'; function init() { /* ... */ }
export { version, init };
export default function runApp() { / ... / }
### ES6 模块导入方式
- **导入命名导出**:
```javascript
import { API_URL, fetchUser } from './api';
-
导入默认导出:
import ApiService from './api'; -
同时导入默认和命名导出:
import ApiService, { version, init } from './api'; -
重命名导入:
import { fetchUser as getUser } from './api'; -
导入整个模块为命名空间:
import * as api from './api'; console.log(api.API_URL); api.fetchUser(123); -
动态导入(异步):
async function loadModule() { const module = await import('./heavy-module.js'); module.doSomething(); }
常见错误与避坑指南
-
在 CommonJS 中错误使用
import
Node.js 默认不识别import,除非启用了 ES 模块。若报错SyntaxError: Cannot use import statement outside a module,请检查:- 文件是否为
.mjs package.json是否有"type": "module"- 是否在浏览器中忘了加
type="module"
- 文件是否为
-
在 ES6 模块中使用
require
ES6 模块环境下无法使用require。若需兼容,可改用动态import()。 -
路径省略扩展名问题
- CommonJS:
require('./lib')会自动尝试lib.js、lib.json、lib.node - ES6 模块:必须显式写出扩展名(如
import './lib.js'),否则浏览器或 Node.js 会报错
- CommonJS:
-
循环依赖处理不同
- CommonJS:返回已执行部分的对象(可能未初始化完成)
- ES6 模块:绑定是“活引用”,但需注意初始化顺序
-
顶层
this的值不同- CommonJS:
this指向module.exports - ES6 模块:
this为undefined
- CommonJS:
互操作:在同一个项目中混合使用
Node.js 允许在 ES6 模块中导入 CommonJS 模块,反之亦然(有限支持)。
ES6 模块导入 CommonJS 模块
假设 cjs-lib.js 是 CommonJS:
// cjs-lib.js
module.exports = { value: 42 };
在 ES6 模块中导入:
// esm-consumer.js
import cjs from './cjs-lib.js'; // 整个 module.exports 作为默认导出
console.log(cjs.value); // 42
// 或使用命名导入(Node.js 会将 CommonJS 的属性提升为命名导出)
import { value } from './cjs-lib.js';
console.log(value); // 42
CommonJS 导入 ES6 模块
需使用动态 import()(因为 CommonJS 是同步的,而 ES6 模块是静态的):
// cjs-consumer.js
async function useESM() {
const esm = await import('./esm-lib.js');
esm.default(); // 调用默认导出
}
注意:Node.js 对这种互操作的支持仍在演进,建议尽量统一模块系统。
选择建议:新项目优先使用 ES6 模块(.js + "type": "module"),因其是标准、支持静态分析、兼容未来生态。遗留项目或重度依赖 npm 包(多为 CommonJS)可继续使用 CommonJS,或通过构建工具(如 Webpack、Vite)统一处理。

暂无评论,快来抢沙发吧!