AMD和CMD是ES6前浏览器模块化方案,核心区别在于执行时机:AMD依赖前置、加载完才执行工厂函数,CMD工厂函数立即执行、require时才加载依赖。

AMD 和 CMD 都是浏览器端为解决脚本依赖与加载顺序问题而诞生的异步模块规范,但它们的设计哲学和执行时机截然不同——不是“谁更好”,而是“谁更适合你的构建链路”。
为什么需要 AMD 和 CMD?
在 ES6 import/export 成为主流前,浏览器没有原生模块系统。多个 标签手动加载易出错:
– 依赖模块没加载完就执行(ReferenceError)
– 加载顺序靠人肉维护,一改就崩
– 无法按需加载、无法隔离作用域
于是 RequireJS(推 AMD)和 SeaJS(推 CMD)分别提出标准化方案,目标一致:让模块可声明依赖、可隔离、可异步加载。
define() 的参数写法暴露核心分歧
两者都用全局 define() 定义模块,但参数结构直接体现理念差异:
-
AMD(RequireJS):依赖数组前置,强制“声明即加载”define(['./utils', './api'], function(utils, api) { // 所有依赖已加载完毕,立即执行 return { init: () => utils.log(api.getData()) }; }); -
CMD(SeaJS):依赖在函数体内按需require(),延迟执行define(function(require, exports, module) { // 此时 utils.js 还没加载 var utils = require('./utils'); // 真到这行才发起请求 var api = require('./api'); exports.init = function() { utils.log(api.getData()); }; });
关键区别不在“是否异步”(二者都是异步加载),而在模块工厂函数何时执行:
– AMD:依赖全部 ready 后才调用 factory,适合“启动即全量加载”的场景
– CMD:factory 立即执行,require() 调用时才触发加载,适合“点击才加载某功能”的懒加载逻辑
立即学习“Java免费学习笔记(深入)”;
实际项目中,你大概率不会直接写 AMD/CMD
现代前端工程几乎不再手写 define(),原因很实在:
- 打包工具(Webpack/Vite)默认以
ESM为输入,自动转译并内联依赖,无需手动require([])或define() -
CommonJS(Node.js 风格)仍常见于服务端或构建脚本,但浏览器运行时需打包转换 - RequireJS/SeaJS 已停止维护(RequireJS 最后更新 2020 年,SeaJS 2017 年停更)
- 真正遗留的老项目若还在用,排查问题要盯住:
–require.config()路径别配错(尤其baseUrl和paths)
–CMD中require()必须写在 factory 函数内,写在外面会报require is not defined
容易被忽略的兼容性陷阱
如果你被迫维护一个老 AMD/CMD 项目,这几个点常导致白屏或静默失败:
-
define()写成箭头函数 →this绑定失效,require拿不到上下文 - AMD 模块里混用
module.exports→ RequireJS 不识别,返回undefined - CMD 模块中
exports和module.exports同时赋值 → SeaJS 以module.exports为准,前者被忽略 - 路径别名没在
require.config()里注册,却写了require('jquery')→ 报 404,而不是提示“未配置 alias”
历史价值在于理解模块演进逻辑;工程实践上,ESM + 构建工具才是今天该投入精力的地方。那些 define([...]) 和 require(['x'], ...),现在更多是调试时需要读懂的“古代码”。











