2011~2013 CMD&UMD
CMD
为什么会有 CMD?
背景问题
2009 年 Node.js 出现 → CommonJS 被推广开来。
但是:
- CommonJS 是同步加载的:
const fs = require('fs');
在服务器上没问题,因为硬盘读文件很快。
- 但放到浏览器就很糟糕:网络请求是异步的,如果同步加载模块会卡死页面。
于是浏览器端需要异步模块化方案,于是有了 AMD(RequireJS,2010)。
AMD 的问题
AMD 采用“依赖前置”:
define(['jquery', 'underscore'], function($, _) {
return {
foo: function() { $('#id').show(); }
};
});
- 优点:浏览器可异步并行加载依赖。
- 缺点:写法别扭,和 CommonJS 差别太大,不符合大多数开发者的直觉。
CMD 的出现(2011,SeaJS)
阿里玉伯(淘宝前端)觉得 AMD 写法“不优雅”,所以提出 CMD(Common Module Definition)。
特点:
- 依赖就近:需要哪个模块时再
require,不像 AMD 一开始就写在 define 里。 - 更接近 CommonJS 的写法,迁移成本低。
例子:
define(function(require, exports, module) {
var $ = require('jquery'); // 就近引用
exports.foo = function() {
$('#id').show();
};
});
👉 这样更“顺手”,也更适合当时的国情(很多中国团队在用 CommonJS 风格)。
CMD 的原理
- CMD 依然基于浏览器的 异步加载。
- 但与 AMD 不同,它在 运行时 才解析
require。- AMD:一开始就知道要加载哪些模块。
- CMD:代码执行到
require('x')才去加载。
⚠️ 这导致 性能差异:AMD 可以并行预加载依赖,CMD 可能要多次网络请求。
所以 CMD 更符合开发习惯,但性能略逊色。
UMD
为什么需要 UMD?
问题背景
2011~2013 年,前端生态混乱:
- Node.js 生态用 CommonJS。
- 浏览器端一部分项目用 AMD(RequireJS),一部分用 CMD(SeaJS)。
- 还有一些老项目,直接把 JS 挂到全局变量上(
window.jQuery)。
→ 前端库作者很痛苦:写一个库,得写三四个版本。
UMD 的出现(2012 左右)
UMD(Universal Module Definition)不是新规范,而是一种兼容模式写法。
例子:
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD
define(['jquery'], factory);
} else if (typeof exports === 'object') {
// CommonJS
module.exports = factory(require('jquery'));
} else {
// 浏览器全局变量
root.myLib = factory(root.jQuery);
}
}(this, function ($) {
return {
foo: function() { $('#id').show(); }
};
}));
👉 这段代码能在:
- RequireJS (AMD) 下运行
- Node.js (CommonJS) 下运行
- 普通浏览器
<script>全局变量方式下运行
CMD & UMD 的地位
- CMD:主要在中国流行,依赖 SeaJS。随着 ES6 模块出现逐渐消失。
- UMD:至今仍然存在,很多经典库(如 Lodash、Moment.js)仍然使用 UMD 打包方式,以便兼容不同环境。
如何看待 CMD & UMD(作为前端工程师)
- CMD:
- 历史意义:让模块化更“接地气”,贴近 CommonJS。
- 学习意义:理解前端模块化演进思路,但不必深入。
- UMD:
- 至今还偶尔能遇到(老库或兼容性考虑)。
- 学习意义:要知道它的存在,能认出那段兼容代码。
总结一下:
- CMD(2011):对 AMD 的改良,更好写,但性能稍差。
- UMD(2012):兼容所有模块化规范的“打补丁方案”。
- 2015 以后:ES Module 出现,AMD/CMD/UMD 逐渐淡出历史舞台。