CommonJS / AMD / UMD

CommonJS、AMD、UMD 三种历史模块系统的关系与适用场景。

#type / concept #status / growing #resource / javascript #resource / nodejs #platform / browser

[!info] related notes

CommonJS / AMD / UMD

一句话定义

  • CommonJS (CJS):Node.js 的同步模块系统,用 require() 导入、module.exports 导出。
  • AMD:浏览器端的异步模块系统,用 define() 定义模块。
  • UMD:同时兼容 CJS 和 AMD 的通用包装模式,用于库发布。

CommonJS 详解

// 导出
const PI = 3.14;
function add(a, b) { return a + b; }
module.exports = { PI, add };

// 也支持分别挂载
exports.PI = PI;
exports.add = add;

// 导入
const math = require('./math');
const { PI, add } = require('./math');

特点

  • 同步加载,适合服务器端(文件已在本地磁盘)。
  • require() 是运行时调用,可以出现在任何位置(if、函数内)。
  • 导入的是值拷贝(基础类型是拷贝,对象是引用的浅拷贝)。
  • 模块执行一次后缓存,再次 require() 返回同一对象。

AMD 详解

// 定义模块
define('math', ['dep1', 'dep2'], function(dep1, dep2) {
  return {
    add: function(a, b) { return a + b; }
  };
});

// 匿名模块(更常见)
define(['jquery'], function($) {
  return function(selector) {
    return $(selector);
  };
});

// RequireJS 加载
require(['math'], function(math) {
  console.log(math.add(1, 2));
});

特点

  • 异步加载,为浏览器设计(网络请求不能阻塞)。
  • define(deps, callback) 显式声明依赖。
  • 依赖加载完成后才执行回调,避免运行时找不到模块。
  • RequireJS 是最流行的 AMD 加载器。

UMD 包装模式

(function(root, factory) {
  if (typeof define === 'function' && define.amd) {
    // AMD 环境(RequireJS)
    define(['jquery'], factory);
  } else if (typeof module === 'object' && module.exports) {
    // CommonJS 环境(Node.js)
    module.exports = factory(require('jquery'));
  } else {
    // 浏览器全局变量
    root.MyLib = factory(root.jQuery);
  }
}(typeof self !== 'undefined' ? self : this, function($) {
  return { init: function() { /* ... */ } };
}));

特点

  • 不是新模块系统,是兼容包装模式
  • 检测当前环境,选择对应的模块加载方式。
  • 今天主要在 npm 包的 UMD 构建产物中看到(如 dist/lib.umd.js)。

今天怎么选

场景推荐
新项目ESM(import / export
Node.js 遗留代码CJS,逐步迁移 ESM
库发布(需要全局变量)UMD 或 ESM + CJS 双产物
读老代码 / 打包产物理解 CJS / AMD / UMD 即可

ESM 迁移注意事项

  • Node.js 中启用 ESM:"type": "module" in package.json 或使用 .mjs 扩展名。
  • CJS require() 在 ESM 中不可直接用,需要 import { createRequire } from 'module'
  • ESM 的 import 是静态的,不能在 if 中使用;CJS 的 require() 是动态的,可以。
  • 混用 CJS 和 ESM 时注意循环依赖行为的差异。
创建于 2025/1/1 更新于 2026/5/27