JavaScript 模块化规范详解:CommonJS、AMD、UMD、CMD 与 ES6 对比

作者:青柠

时间:2026-10-26

浏览:359

本文深入解析 JavaScript 五大模块化规范:CommonJS、AMD、UMD、CMD 及 ES6。详细阐述各规范的加载机制、优缺点及适用场景,重点对比 AMD 与 CMD 的执行差异,以及 ES6 模块在静态分析与加载方式上与 CommonJS 的本质区别,帮助开发者根据项目需求选择最佳方案。

文章目录

JavaScript 语言的发展历程

本文将系统介绍几种主流的 JavaScript 模块化规范,并分析它们各自的优缺点及核心差异。

JavaScript 最初仅作为简单的脚本语言存在,但随着浏览器功能的日益强大,其承载复杂应用的能力得到认可。为了便于代码维护,开发者开始将不同功能抽离至独立的 JS 文件中。然而,随着项目复杂度提升,HTML 中引入大量 JS 文件导致命名冲突、作用域污染等问题频发,模块化概念及实现方案因此应运而生。

模块化概述

模块化不仅是一种管理方式,更是一种生产模式和解决方案。一个模块通常对应实现特定功能的文件,开发者可以按需引入所需模块以复用他人代码。为确保模块间协同工作,必须遵循特定的规范,这催生了后续一系列标准化的模块系统。

1. CommonJS 规范

CommonJS 主要应用于 node 服务端开发环境。在 CommonJS 中,每个文件被视为一个独立的模块,拥有独立的作用域。模块通过 module.exports 机制向外暴露 public 成员。

此外,CommonJS 规范通过 require 函数引入模块依赖。require 函数可用于加载 node 内置模块、项目自定义模块以及通过 npm 管理的第三方模块。

定义模块示例:

// 定义模块 math.js
var basicNum = 0;
function add(a, b) {
  return a + b;
}
// 在这里写上需要向外暴露的函数、变量
module.exports = { 
  add: add,
  basicNum: basicNum
}

加载模块示例:

// 引入 math.js 模块
var math = require('./math');
math.add(2, 3); // 5

CommonJS 的优点:

  • 语法简单且易于使用
  • 服务端模块复用性高

CommonJS 的缺点:

  • 同步加载机制不适合浏览器环境
  • 无法实现非阻塞的并行模块加载

2. AMD 规范

AMD 全称为 (Asynchronous Module Definition),即“异步模块定义”。该规范采用异步方式加载模块,模块加载过程不会阻塞后续代码的执行。所有依赖该模块的语句均封装在回调函数中,待模块加载完成后,回调函数才会执行。

在 AMD 规范中,我们使用 define 定义模块,使用 require 加载模块,但是不同于 CommonJS,它要求两个参数:

定义模块语法:

define(id?, dependencies?, factory);
  • id 为可选的模块名称。若未提供,模块名默认为加载器请求的脚本名;若提供,则必须为顶级绝对路径。
  • dependencies 为可选的依赖模块数组。依赖模块按数组顺序优先执行,其执行结果依次作为参数传入 factory。
  • factory 为模块初始化执行的函数或对象,是必需参数。

加载模块语法:

require([module], callback);

第一个参数 module 为包含待加载模块的数组;第二个参数 callback 为加载成功后的回调函数。若将前述 CommonJS 写法改写为 AMD 形式,代码如下:

require(['./math'], function (math) {
  math.add(2, 3);
});

AMD 的优点:

  • 适合浏览器环境下的异步模块加载
  • 支持并行加载多个模块

AMD 的缺点:

  • 增加了开发成本
  • 不符合通用的模块化思维习惯

3. UMD 规范

UMD 全称为 (Universal Module Definition),即“通用模块定义”。UMD 是 AMD 与 CommonJS 的混合体。AMD 侧重浏览器优先与异步加载;CommonJS 侧重服务器优先与同步加载。

为实现通用性,UMD 策略如下:首先判断是否支持 node 模块环境,若支持则采用 node 方式;其次判断是否支持 AMD,若支持则采用 AMD 方式加载。这种兼容多种规范的策略被称为 UMD。

UMD 示例代码:

(function (window, factory) {
  if (typeof exports === "object") {
    // CommonJS
    module.exports = factory();
  } else if (typeof define === "function" && define.amd) {
    // AMD
    define(factory);
  } else {
    // 浏览器全局定义
    window.eventUtil = factory();
  }
})(this, function () {
  // do something
});

4. CMD 规范

CMD 全称为 (Common Module Definition),即“公共模块定义”。CMD 可能是在 CommonJS 基础上抽象出的一套模块化语法定义与使用标准。

在 CMD 规范中,一个文件对应一个模块。

定义模块语法:

define(factory);

define 接收 factory 参数,该参数可以是函数、对象或字符串。

  • 当 factory 为对象或字符串时,表示该模块的接口即为该对象或字符串。
  • 当 factory 为函数时,表示该模块的构造方法。执行该构造方法可获得模块对外提供的接口。factory 执行时默认传入三个参数:require、exports 和 module。其中 require 用于加载其他模块,exports 用于向外提供模块接口,module 为存储当前模块相关属性和方法的对象。

加载模块方式:

可通过 SeaJs 的 use 方法加载模块,语法如下:

seajs.use([module], callback);

CMD 的优点:易于在 node 环境中运行

CMD 的缺点:依赖 SPM 打包工具,模块加载逻辑相对复杂

5. ES6 模块化

ES6 模块的设计思想强调“静态化”,旨在编译阶段即可确定模块依赖关系及输入输出变量。相比之下,CommonJS 和 AMD 模块需在运行时才能确定这些内容。

在 ES6 中,使用 export 关键字导出模块,使用 import 关键字引入模块。

引入模块示例:

// ES6模块
import { stat, exists, readFile } from 'fs';

上述代码实质是从 fs 模块中加载 3 个方法,其余方法不加载。这种加载方式称为“编译时加载”或“静态加载”,即 ES6 可在编译阶段完成模块加载,效率高于 CommonJS 模块的加载方式。这也导致无法直接引用 ES6 模块本身,因为其并非对象。

导出模块示例:

let firstName = 'Zhou';
let lastName = 'ShuYi';
let year = 1994;
export { firstName, lastName, year };

上述代码在 export 后使用大括号指定输出的一组变量。export 除输出变量外,还可输出函数或类。

ES6 模块的优点:易于进行静态分析

ES6 模块的缺点:原生浏览器端尚未完全实现该标准

AMD 与 CMD 的区别

对于依赖模块,AMD 采用“提前执行”策略,而 CMD 采用“延迟执行”策略。

AMD 推崇“依赖前置”,CMD 推崇“依赖就近”。

AMD 的 API 默认一个函数处理多种情况,CMD 的 API 严格区分,推崇职责单一。

ES6 模块与 CommonJS 模块的差异

CommonJS 模块输出的是“值的拷贝”,而 ES6 模块输出的是“值的引用”。

CommonJS 模块采用“运行时加载”,ES6 模块采用“编译时输出接口”。

CommonJS 模块的 require() 命令为“同步加载”,而 ES6 模块的 import 命令为“异步加载”,具有独立的模块依赖解析阶段。

总结

以上为笔者对 JavaScript 模块化规范的解读,更多关于 JS 模块化规范的资料请关注本站其他相关文章!

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章