人人都会AI编程

19.3 AMD / CMD 规范:浏览器端异步模块化方案

更新时间:2026-07-11

在模块化的演进历程中,CommonJS 率先解决了服务器端模块化的问题,但它的同步加载特性天然不适合浏览器环境——网络请求是异步的,不可能让 JS 引擎停下等待文件加载完成再执行。于是,社区先后提出了两套专为浏览器设计的异步模块化规范:AMD 和 CMD。它们代表了前端工程化早期的重要探索。

19.3.1 AMD 规范

AMD(Asynchronous Module Definition,异步模块定义)由 RequireJS 的作者 James Burke 提出,其核心理念只有一条:模块和它的依赖全部异步加载,加载完成后立即执行模块逻辑。这意味着模块的依赖可以并行加载,无需等待前一个模块完成。

基本语法:define(id?, dependencies?, factory)

// 定义一个模块,依赖 jQuery 和 lodash
define(['jquery', 'lodash'], function($, _) {
  // 这里 $ 和 _ 已经加载并执行完毕
  return {
    doSomething: function() {
      return _.map([1,2,3], n => n * 2);
    }
  };
});

// 定义无依赖模块
define(function() {
  return { name: 'I am a simple module' };
});

// 加载并使用模块
require(['moduleA', 'moduleB'], function(a, b) {
  a.doSomething();
});

RequireJS 的实现机制
RequireJS 会扫描 define 中声明的依赖数组,通过动态创建 <script> 标签加载对应的 JS 文件。所有依赖并行发起请求,等全部脚本加载完成后再执行工厂函数,并将返回值(或导出对象)传入回调。这种“依赖前置”的方式保证了模块执行前所有依赖已就绪,但缺点也明显:即使某些依赖在特定分支中可能不会被使用,它们也会在第一时间被加载。

19.3.2 CMD 规范:从“依赖前置”到“就近依赖”

CMD(Common Module Definition,通用模块定义)由 SeaJS 的作者玉伯提出,其设计思想与 AMD 一脉相承(都基于异步加载),但有一个核心差异:依赖就近声明,用到时才加载

SeaJS 的基本语法:define(factory)

define(function(require, exports, module) {
  // 可以在需要时才 require 依赖
  var $ = require('jquery');       // 同步方式写异步加载
  // 实际执行的是异步加载,但开发者感知为同步

  exports.sayHello = function() {
    console.log('hello');
  };

  // 如果某些依赖在条件分支中才用到,可以就近 require
  if (someCondition) {
    var extra = require('extraLib');
    extra.help();
  }
});

CMD 的 factory 函数接受 requireexportsmodule 三个参数,其中 require 是一个特殊的函数:当代码执行到它时,SeaJS 会暂停当前模块的执行,异步加载对应脚本,加载完成后继续执行后续代码。这种写法让开发者能够按需加载依赖,代码风格更接近 CommonJS,心智模型更自然。然而,由于加载过程是串行的(依赖之一加载完才加载下一个),在某些场景下首屏加载速度可能不如 AMD 的并行加载。

19.3.3 AMD 与 CMD 的核心差异对比

| 特性 | AMD | CMD |
|---|---|---|
| 依赖声明方式 | 前置(定义时传入依赖数组) | 就近(工厂函数内部按需 require) |
| 模块执行时机 | 依赖全部加载完毕后立即执行 | 依赖按书写顺序逐个加载并执行 |
| 加载策略 | 依赖并行加载 | 依赖串行加载(单个模块内可能的优化) |
| 代表实现 | RequireJS | SeaJS |
| 使用体验 | 写法略繁琐,但加载效率高 | 写法自然,但加载效率稍低 |

19.3.4 历史价值与现实意义

在 ES6 模块标准落地之前,AMD 和 CMD 是前端大型项目模块化的实际承担者。尤其是 RequireJS,在 jQuery、Backbone 时代曾大规模应用,帮助无数项目从“脚本堆砌”过渡到“模块化管理”。CMD 的 SeaJS 则在国内一度拥有众多支持者,其“就近依赖”的设计影响了后续的模块规范讨论。

然而,随着 ES Modules(ESM)成为官方标准,浏览器原生支持静态 import/export,配合打包工具(Webpack、Vite)对异步加载的成熟方案,AMD/CMD 已经逐渐退出历史舞台。如今新项目几乎不会主动采用 RequireJS 或 SeaJS,但了解它们仍然必要:

  • 理解前端工程化的演化脉络:它们展示了“浏览器环境下如何实现模块化”这一问题的多种解答思路。
  • 维护老旧项目:如果你接手一个旧版系统,有可能会碰到 AMD 或 CMD 的结构。
  • 启发后续标准设计:AMD 的动态异步加载特性和 CMD 的“用 CommonJS 风格写异步”思想,在 ESM 的动态 import() 中都能找到影子。

一句话总结:AMD 与 CMD 是过渡时代的前端模块化方案,它们用异步加载解决了浏览器端的依赖问题,为后来 ESM 的落地铺平了道路。作为历史知识消化即可,无需在新项目中实践。