AMD、CMD、UMD、ESM和CommonJS
好的,这是一个关于 JavaScript 模块化规范的全面解析。这些规范的出现都是为了解决 JavaScript 在大型项目中代码组织、依赖管理和作用域隔离的问题。
下面我将详细解释 CommonJS、AMD、CMD、UMD 和 ESM,并说明它们之间的关系和区别。
1. CommonJS - 同步的服务器端模块规范
CommonJS 规范主要应用于服务器端,例如 Node.js 环境。它的设计理念是模块的加载是同步的,因为在服务器端,模块文件都存放在本地磁盘,读取速度非常快,同步加载不会造成性能问题。
特点:
- 同步加载:执行
require时,会阻塞后续代码的执行,直到模块被加载并执行完毕。 - 模块输出的是值的拷贝:一旦输出一个值,模块内部的变化就影响不到这个值(除非是导出一个函数,通过函数来获取内部状态)。
- 每个文件都是一个模块:拥有独立的作用域。
语法:
- 导出模块:使用
module.exports或exports// math.js function add(a, b) { return a + b; } module.exports = { add: add }; // 或者 exports.add = add; - 导入模块:使用
require// app.js const math = require('./math.js'); console.log(math.add(2, 3)); // 5
适用环境: Node.js。
2. AMD - 异步的浏览器端模块规范
AMD 规范主要为了解决浏览器环境的模块化问题。由于浏览器加载文件需要从服务器获取,如果采用同步方式会导致页面“假死”,因此 AMD 的设计理念是异步加载。
代表库: RequireJS
特点:
- 异步加载:不阻塞页面渲染,依赖模块加载完毕后执行回调函数。
- 显式地声明依赖。
语法:
- 定义模块:使用
define// 定义一个依赖 jQuery 的模块 define(['jquery'], function($) { // 模块功能 function myFunc() { $('#content').html('Hello, AMD!'); } // 暴露公共接口 return { myFunc: myFunc }; }); - 加载模块:使用
require// 加载并使用模块 require(['myModule'], function(myModule) { myModule.myFunc(); });
适用环境: 浏览器。
3. CMD - 通用的模块规范
CMD 也是国内发展出来的一种浏览器端模块规范,结合了 CommonJS 和 AMD 的特点。
代表库: Sea.js
特点:
- 推崇依赖就近、延迟执行。在需要依赖时,随时
require。 - 语法上更接近 CommonJS。
语法:
- 定义模块:使用
definedefine(function(require, exports, module) { // 同步获取依赖 var $ = require('jquery'); // 异步获取依赖 require.async('./moduleB', function(moduleB) { // ... }); // 暴露接口 exports.someMethod = function() { $('#content').html('Hello, CMD!'); }; });
AMD 与 CMD 的主要区别:
- 依赖声明:AMD 推崇依赖前置,在定义模块时就声明所有依赖。CMD 推崇依赖就近,在代码需要时再
require。 - 执行时机:AMD 的模块加载完成后会立即执行。CMD 的模块加载后默认不执行,只有在
require时才会执行。
4. UMD - 通用模块定义
UMD 不是一个独立的规范,而是一种模式或者代码包装技术。它的目标是让同一个模块代码可以在多种环境中运行(无论是 CommonJS、AMD 还是全局变量)。
原理: 通过判断当前环境支持的模块系统,将模块“适配”到该系统中。
示例模板:
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD 环境 (如 RequireJS)
define(['jquery'], factory);
} else if (typeof module === 'object' && module.exports) {
// CommonJS 环境 (如 Node.js)
module.exports = factory(require('jquery'));
} else {
// 浏览器全局变量
root.myModule = factory(root.jQuery);
}
}(this, function ($) {
// 模块的业务代码
function myFunc() {
$('body').html('Hello, UMD!');
}
// 暴露公共接口
return {
myFunc: myFunc
};
}));
适用环境: 同时需要支持浏览器和 Node.js 的库,例如 React、Vue 等。
5. ESM - 官方的模块标准
ESM 是 ECMAScript 2015 (ES6) 官方推出的模块标准,旨在成为浏览器和服务器通用的模块解决方案。它是 JavaScript 语言的原生模块系统。
特点:
- 静态化:在编译时(代码执行前)就能确定模块的依赖关系,以及输入和输出的变量。这使得静态分析、Tree Shaking(摇树优化)成为可能。
- 模块输出的是值的引用。
- 异步加载,也可以同步执行。
- 语法简洁,是未来的绝对主流。
语法:
- 导出模块:使用
export/export default// lib.js // 命名导出 export const pi = 3.14; export function add(a, b) { return a + b; } // 默认导出 export default function() { console.log('Hello'); } - 导入模块:使用
import// main.js // 导入命名导出 import { pi, add } from './lib.js'; // 导入默认导出 myDefault from './lib.js'; console.log(pi); // 3.14 add(1, 2); myDefault();
在浏览器中使用:
需要在 <script> 标签中加上 type="module" 属性。
<script type="module" src="main.js"></script>
适用环境: 现代浏览器和现代 JavaScript 运行环境(Node.js 也已稳定支持)。
总结与对比
| 规范 | 主要环境 | 加载方式 | 特点 | 关键字 |
|---|---|---|---|---|
| CommonJS | Node.js | 同步 | 简单,适合服务器 | require, module.exports |
| AMD | 浏览器 | 异步 | 依赖前置,适合浏览器 | define, require |
| CMD | 浏览器 | 异步 | 依赖就近,延迟执行 | define, require |
| UMD | 通用 | 均可 | 兼容多种环境的包装方案 | - |
| ESM | 通用(现代) | 静态/异步 | 官方标准,静态分析,未来趋势 | import, export |
发展趋势
- ESM 是未来:随着 ES6+ 的普及和构建工具(Webpack、Vite、Rollup)的成熟,ESM 已经成为新项目的首选。
- CommonJS 在 Node.js 生态中依然庞大,但 Node.js 自身正在积极推动向 ESM 迁移。
- AMD/CMD 在现代前端开发中已很少使用,主要存在于一些遗留的老项目中。
- UMD 对于需要分发、兼容多种环境的库来说,仍然非常有用。
简单来说,对于现代开发:写库用 UMD 或 ESM 分发,写应用直接用 ESM。

浙公网安备 33010602011771号