CommonJS / AMD / UMD
CommonJS、AMD、UMD 三种历史模块系统的关系与适用场景。
#synthesis
#dev
#growing
#javascript
#nodejs
#browser
[!info] related notes
- 所属 MOC: Node.js MOC
- 上位主题: ECMAScript 模块模式, Node.js 中的 JavaScript
- 并列概念: ES6 模块导入导出, HTML 中的 JS 脚本
- Node 配置: package-json
- 关系笔记: ECMAScript 模块模式, JavaScript的工程化与运行时
CommonJS / AMD / UMD
一句话定义
- CommonJS (CJS):Node.js 的同步模块系统,用
require()导入、module.exports导出。 - AMD:浏览器端的异步模块系统,用
define()定义模块。 - UMD:同时兼容 CJS 和 AMD 的通用包装模式,用于库发布。
CommonJS 与 AMD 的边界
CommonJS 在 Node 中通过同步 require() 返回 module.exports,并按解析后的文件名缓存。语法、对象共享、exports 重绑定与循环依赖细节统一在该概念页维护。
AMD 为异步加载依赖提供声明接口,常用于由 RequireJS 等加载器管理的旧浏览器工程。两者的 require 不是同一个接口:Node 通常接收模块标识符并返回导出值;RequireJS 可接收依赖数组并在回调里交付依赖。
AMD 详解
// 定义模块
define('math', ['dep1', 'dep2'], function(dep1, dep2) {
return {
add: function(a, b) { return a + b; }
};
});
// 匿名模块(更常见)
define(['jquery'], function($) {
return function(selector) {
return $(selector);
};
});
// RequireJS 加载
require(['math'], function(math) {
console.log(math.add(1, 2));
});
特点:
- 异步加载,为浏览器设计(网络请求不能阻塞)。
define(deps, callback)显式声明依赖。- 依赖加载完成后才执行回调,避免运行时找不到模块。
- RequireJS 是常见的 AMD 加载器;浏览器本身不提供
define。
UMD 包装模式
(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.MyLib = factory(root.jQuery);
}
}(typeof self !== 'undefined' ? self : this, function($) {
return { init: function() { /* ... */ } };
}));
特点:
- 不是新模块系统,是兼容包装模式。
- 检测当前环境,选择对应的模块加载方式。
- 今天主要在 npm 包的 UMD 构建产物中看到(如
dist/lib.umd.js)。
今天怎么选
| 场景 | 推荐 |
|---|---|
| 新项目 | ESM(import / export) |
| Node.js 遗留代码 | CJS,逐步迁移 ESM |
| 库发布(需要普通 script 全局变量) | 提供 UMD 或 IIFE 构建;ESM + CJS 双产物本身不提供全局入口 |
| 读老代码 / 打包产物 | 理解 CJS / AMD / UMD 即可 |
ESM 迁移注意事项
- Node.js 中启用 ESM:
"type": "module"inpackage.json或使用.mjs扩展名。 - 原生 Node ESM 没有内置
require变量;需要时从node:module导入createRequire并用createRequire(import.meta.url)创建。 - ESM 的静态
import声明只能在模块顶层;条件加载可以用import()表达式,CJS 的require()也可以在条件分支中调用。 - 混用 CJS 和 ESM 时注意循环依赖行为的差异。
学习检查
- AMD 的加载器与 UMD 的环境检测分别负责什么?
- 为什么 ESM + CJS 双产物不能直接替代普通 script 的全局入口?