主流模块规范对比
以下是主流前端模块规范(CommonJS、AMD、CMD、ES Modules)的对比分析,从语法、适用场景、特性等维度展开:
一、核心语法对比
| 规范 | 导入语法 | 导出语法 | 特点 |
|---|---|---|---|
| CommonJS | const module = require('module') | module.exports = value 或 exports.key = value | 同步加载,适用于服务器端(Node.js) |
| AMD | require(['module'], callback) | define(['dep'], function(dep) { return {} }) | 异步加载,依赖前置 |
| CMD | define(function(require, exports) { const dep = require('dep') }) | exports.key = value | 异步加载,依赖就近 |
| ES Modules | import { name } from 'module' 或 import('./module').then(...) | export const name = 'value' 或 export default value | 静态分析,支持 Tree-shaking |
二、关键特性对比
| 特性 | CommonJS | AMD | CMD | ES Modules |
|---|---|---|---|---|
| 加载方式 | 同步(阻塞) | 异步(非阻塞) | 异步(非阻塞) | 静态(编译时)+ 动态(运行时) |
| 适用环境 | 服务器端(Node) | 浏览器端 | 浏览器端 | 浏览器 + 服务器(需配置) |
| 依赖声明位置 | 顶部集中声明 | 定义时声明 | 使用时声明 | 顶部集中声明 + 动态导入 |
| 执行时机 | 导入时执行 | 定义时执行 | 使用时执行 | 静态分析,按顺序执行 |
| 动态导入支持 | 不支持 | 支持 | 支持 | 支持(import('./module')) |
| Tree-shaking | 不支持 | 不支持 | 不支持 | 支持(静态结构特性) |
| 循环依赖处理 | 允许但需谨慎 | 较复杂 | 较简单 | 支持(按执行顺序) |
三、典型场景对比
| 场景 | 最佳选择规范 | 原因 |
|---|---|---|
| Node.js 应用 | CommonJS 或 ES Modules | Node.js 原生支持 CommonJS,通过配置支持 ES Modules |
| 浏览器单页应用 | ES Modules + 打包工具(Webpack/Vite) | 现代浏览器原生支持,打包工具优化加载性能 |
| 浏览器异步加载 | AMD(RequireJS)或动态导入 | 需异步加载大量模块时使用 |
| 库开发 | ES Modules + Rollup | 利用 Tree-shaking,生成精简代码 |
| 兼容多环境 | UMD(通用模块定义) | 同时支持 CommonJS、AMD 和全局变量 |
四、示例代码对比
1. CommonJS
javascript
// math.js
const add = (a, b) => a + b;
module.exports = { add };
// main.js
const math = require("./math.js");
console.log(math.add(1, 2)); // 32. AMD(RequireJS)
javascript
// math.js
define(function () {
const add = (a, b) => a + b;
return { add };
});
// main.js
require(["math"], function (math) {
console.log(math.add(1, 2)); // 3
});3. CMD(SeaJS)
javascript
// math.js
define(function (require, exports, module) {
const add = (a, b) => a + b;
exports.add = add;
});
// main.js
define(function (require, exports, module) {
const math = require("./math"); // 依赖就近声明
console.log(math.add(1, 2)); // 3
});4. ES Modules
javascript
// math.js
export const add = (a, b) => a + b;
// main.js
import { add } from "./math.js";
console.log(add(1, 2)); // 3
// 动态导入
async function loadMath() {
const { add } = await import("./math.js");
console.log(add(3, 4)); // 7
}五、兼容性与生态系统
| 规范 | 原生支持环境 | 打包工具支持 | 生态成熟度 |
|---|---|---|---|
| CommonJS | Node.js(默认) | Webpack、Rollup、Browserify | 高 |
| AMD | 需 RequireJS 等库 | Webpack、Rollup | 中 |
| CMD | 需 SeaJS 等库 | 部分支持(如 Webpack) | 低 |
| ES Modules | 现代浏览器、Node.js(需配置) | Webpack、Vite、Rollup | 高(未来趋势) |
六、选择建议
新项目:
- 优先使用 ES Modules(浏览器 + Node.js),配合 Vite 或 Webpack 打包。
- 若需兼容旧浏览器,使用 Babel 转译。
Node.js 项目:
- 若无需兼容旧版本 Node,直接使用 ES Modules(通过
type: "module"配置)。 - 若需兼容,继续使用 CommonJS。
- 若无需兼容旧版本 Node,直接使用 ES Modules(通过
浏览器异步加载场景:
- 优先使用 ES Modules 动态导入,而非 AMD。
库开发:
- 使用 ES Modules 编写,通过 Rollup 打包为多种格式(ES、CommonJS、UMD)。
兼容多环境:
- 使用 UMD 格式,或通过打包工具输出多种格式。
七、总结
| 规范 | 优势 | 劣势 | 适用场景 |
|---|---|---|---|
| CommonJS | 简单直接,Node.js 原生支持 | 同步加载,不适合浏览器 | Node.js 应用 |
| AMD | 异步加载,适合浏览器 | 语法复杂,生态逐渐式微 | legacy 浏览器项目 |
| CMD | 依赖就近,灵活控制加载时机 | 生态小众,工具支持有限 | 特定场景(如 SeaJS 历史项目) |
| ES Modules | 语言标准,静态分析,Tree-shaking | 需打包工具支持,旧浏览器不兼容 | 现代前端应用与库开发 |
随着 ES Modules 的普及,未来模块化将趋向统一,建议优先掌握 ES Modules,并了解 CommonJS 以应对 Node.js 场景。