CommonJS / AMD / UMD
CommonJS、AMD、UMD 三种历史模块系统的关系与适用场景。
#type / concept
#status / growing
#resource / javascript
#resource / nodejs
#platform / browser
[!info] related notes
- 所属 MOC: javascript-in-nodejs-moc
- 上位主题: ecmascript-module-pattern, javascript-in-nodejs
- 并列概念: es6-module-import-export, html-js-scripts
- Node 配置: package-json
- 关系笔记: ecmascript-module-pattern, JavaScript的工程化与运行时
CommonJS / AMD / UMD
一句话定义
- CommonJS (CJS):Node.js 的同步模块系统,用
require()导入、module.exports导出。 - AMD:浏览器端的异步模块系统,用
define()定义模块。 - UMD:同时兼容 CJS 和 AMD 的通用包装模式,用于库发布。
CommonJS 详解
// 导出
const PI = 3.14;
function add(a, b) { return a + b; }
module.exports = { PI, add };
// 也支持分别挂载
exports.PI = PI;
exports.add = add;
// 导入
const math = require('./math');
const { PI, add } = require('./math');
特点:
- 同步加载,适合服务器端(文件已在本地磁盘)。
require()是运行时调用,可以出现在任何位置(if、函数内)。- 导入的是值拷贝(基础类型是拷贝,对象是引用的浅拷贝)。
- 模块执行一次后缓存,再次
require()返回同一对象。
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 加载器。
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 |
| 库发布(需要全局变量) | UMD 或 ESM + CJS 双产物 |
| 读老代码 / 打包产物 | 理解 CJS / AMD / UMD 即可 |
ESM 迁移注意事项
- Node.js 中启用 ESM:
"type": "module"inpackage.json或使用.mjs扩展名。 - CJS
require()在 ESM 中不可直接用,需要import { createRequire } from 'module'。 - ESM 的
import是静态的,不能在if中使用;CJS 的require()是动态的,可以。 - 混用 CJS 和 ESM 时注意循环依赖行为的差异。