前端模块化的发展历程是怎样的?AMD、CMD、UMD 是什么?
模块化发展历程
全局变量 → IIFE → CommonJS → AMD/CMD → UMD → ES Modules1. 全局变量(最早期)
// utils.js
var utils = { add: function(a, b) { return a + b } }
// app.js
utils.add(1, 2)- ❌ 命名冲突
- ❌ 依赖管理困难
2. IIFE(立即执行函数)
// utils.js
var utils = (function() {
function add(a, b) { return a + b }
return { add }
})()- ✅ 避免全局污染
- ❌ 依赖管理仍手动
3. CommonJS(Node.js)
// 同步加载,适合服务端
const fs = require('fs')
module.exports = { add: (a, b) => a + b }- ✅ Node.js 原生支持
- ❌ 不适合浏览器(同步加载阻塞)
4. AMD(Asynchronous Module Definition)
RequireJS 提出的浏览器异步模块规范:
// 定义模块
define('math', [], function() {
return { add: function(a, b) { return a + b } }
})
// 引入模块
require(['math'], function(math) {
math.add(1, 2)
})- ✅ 异步加载,适合浏览器
- ✅ 依赖前置
- ❌ 语法繁琐
- ❌ 已过时
5. CMD(Common Module Definition)
SeaJS 提出的规范,类似 AMD 但依赖就近:
define(function(require, exports, module) {
var math = require('math')
exports.add = function(a, b) { return a + b }
})- ✅ 依赖就近,按需加载
- ❌ 已过时
6. UMD(Universal Module Definition)
兼容 CommonJS、AMD 和全局变量的通用方案:
(function(root, factory) {
if (typeof exports === 'object' && typeof module === 'object') {
// CommonJS
module.exports = factory()
} else if (typeof define === 'function' && define.amd) {
// AMD
define([], factory)
} else {
// 全局变量
root.MyModule = factory()
}
})(this, function() {
return { add: function(a, b) { return a + b } }
})- ✅ 一套代码到处运行
- ❌ 代码冗长
7. ES Modules(ES6+)
// math.mjs
export function add(a, b) { return a + b }
// app.mjs
import { add } from './math.mjs'- ✅ 语言层面支持
- ✅ 静态分析(Tree Shaking)
- ✅ 浏览器 + Node.js 统一
- ✅ 异步加载(import())
对比总结
| 规范 | 加载方式 | 适用 | 现状 |
|---|---|---|---|
| CommonJS | 同步 | Node.js | 仍在使用 |
| AMD | 异步 | 浏览器 | 已过时 |
| CMD | 异步 | 浏览器 | 已过时 |
| UMD | 兼容 | 通用 | 库的降级 |
| ESM | 静态+异步 | 通用 | ✅ 推荐 |
为什么 ESM 是最终方案
- 语言原生支持,不需要库
- 静态分析:编译时确定依赖
- Tree Shaking:摇除未使用代码
- 浏览器原生支持:
<script type="module"> - Node.js 支持:
"type": "module"