前端模块化发展(4):2010年前后AMD(RequireJS)

54 阅读3分钟

2010年前后 AMD(RequireJS)

CommonJS与AMD

时间线

  • 2009年:Node.js 发布,同时社区提出 CommonJS 规范(最初叫 ServerJS,后来改名)。
    • 目标:给 服务器端 JavaScript 一个统一的模块系统。
    • 特点:require 同步加载,本地文件系统读取,没问题。
  • 2010年前后:前端社区发现 CommonJS 不适合浏览器(因为浏览器加载 JS 必须异步)。
    • 于是提出 AMD(Asynchronous Module Definition),典型实现就是 RequireJS
    • 目标:解决 浏览器端 的异步加载和模块组织问题。

👉 先有 CommonJS(服务端),后有 AMD(浏览器端)。
可以说 AMD 是“把 CommonJS 思路搬到浏览器时,发现同步行不通,只好改成异步”的产物。

差异简述

  • CommonJS:面向服务器,关注“文件系统同步加载”。
  • AMD:面向浏览器,关注“网络异步加载”。
  • 模块化项目的运行:取决于运行环境,
    • 在服务器上跑 → 需要服务器端模块化(CommonJS / ES Module)。
    • 在浏览器里跑 → 需要浏览器端模块化(AMD / ES Module)。
    • 如果项目是 全栈,那就两个环境都得有自己的模块化方案。

背景:为什么要有 AMD?

AMD(Asynchronous Module Definition),也就是 RequireJS 所代表的规范。它是前端在 浏览器端 尝试模块化的一次重要实践,出现在 2009~2010s 前后,正是 前端工程化的起点之一。

前面我们说过 CommonJS(Node.js 的 require):

  • 特点:同步加载(运行到 require 才去拿模块)。
  • 问题:浏览器里用不了。

为什么?

  • 浏览器的 JS 加载是通过 <script> 标签请求外部文件。
  • HTTP 请求是 异步 的,如果在浏览器里用 CommonJS:
const math = require('./math.js');
console.log(math.add(1, 2));

浏览器必须“停下来等待 math.js 下载完”,这会阻塞页面,体验极差。

👉 于是社区提出 AMD既然浏览器加载 JS 必然是异步的,那就让模块定义和依赖加载都异步化。


AMD 的核心思想

  • 异步加载:模块不会阻塞页面渲染。
  • 依赖提前声明:你要用什么模块,必须在 define 时声明。
  • 模块工厂函数:依赖加载完成后,传入工厂函数里,执行并返回模块。

RequireJS 的基本写法

定义模块

// math.js
define([], function() {
  function add(a, b) { return a + b; }
  function sub(a, b) { return a - b; }
  return { add, sub };
});

使用模块

// main.js
define(['math'], function(math) {
  console.log(math.add(1, 2)); // 输出 3
});

👉 核心:

  1. define([...依赖...], function(...参数...){...})
    • 依赖会先异步加载
    • 然后把模块对象传入工厂函数的参数
  2. 加载是异步的,不阻塞页面。

RequireJS 的加载流程

  1. 页面引入 require.js(核心库)。
<script data-main="main" src="require.js"></script>
- `data-main="main"` 表示从 `main.js` 作为入口模块开始加载。

2. RequireJS 读取 main.js,发现它 define(['math'], ...)。 3. RequireJS 动态创建 <script src="math.js"> 标签,异步下载 math.js。 4. 等 math.js 加载完成后,执行它的工厂函数,返回模块对象。 5. 把 math 传给 main.js 的回调函数,继续执行逻辑。

👉 整个过程全是异步的,不会阻塞页面。


AMD 的优点

  • 非阻塞加载:适合浏览器环境。
  • 依赖提前声明:一眼能看出模块依赖关系。
  • 跨文件模块化:终于摆脱了把所有 JS 写在一个文件里的时代。
  • 社区推动:RequireJS 在当时是最流行的前端模块加载器。

AMD 的缺点

  • 语法冗长:每个模块都得包裹 define([...], function(){...})
  • 可读性差:嵌套回调多,容易形成“回调地狱”。
  • 依赖顺序问题:虽然是异步,但依赖必须显式声明,否则无法保证顺序。
  • 与 Node.js 不兼容:AMD(浏览器) vs CommonJS(服务器),生态割裂。

总结

  • AMD(RequireJS) 是前端模块化的 过渡方案
  • 它解决了 浏览器端 JS 异步加载 的问题,让模块化在前端真正落地。
  • 但它的语法和体验并不理想,后来才有了 CMD(SeaJS,中国提出,更贴近 CommonJS),以及最终的 ES Module(官方标准化)