前端模块化规范

CommonJS、AMD、CMD、UMD、ESM 的区别与使用

问题

前端有哪些模块化规范?它们的区别是什么?

解答

CommonJS

Node.js 采用的规范,同步加载模块。

// math.js - 导出模块
const add = (a, b) => a + b;
const subtract = (a, b) => a - b;

module.exports = { add, subtract };
// 或者
exports.add = add;
exports.subtract = subtract;

// main.js - 导入模块
const math = require('./math.js');
console.log(math.add(1, 2)); // 3

// 解构导入
const { add } = require('./math.js');

特点

  • 同步加载,适合服务端
  • 运行时加载,可以动态 require
  • 导出的是值的拷贝

AMD (Asynchronous Module Definition)

异步模块定义,RequireJS 实现,适合浏览器。

// 定义模块 math.js
define('math', [], function() {
  return {
    add: function(a, b) { return a + b; },
    subtract: function(a, b) { return a - b; }
  };
});

// 定义有依赖的模块
define('calculator', ['math'], function(math) {
  return {
    calculate: function(a, b) {
      return math.add(a, b);
    }
  };
});

// 使用模块
require(['calculator'], function(calculator) {
  console.log(calculator.calculate(1, 2)); // 3
});

特点

  • 异步加载,不阻塞页面
  • 依赖前置,提前执行

CMD (Common Module Definition)

SeaJS 实现,与 AMD 类似但依赖就近、延迟执行。

// 定义模块
define(function(require, exports, module) {
  // 依赖就近,用到时再 require
  var math = require('./math');
  
  exports.calculate = function(a, b) {
    return math.add(a, b);
  };
});

// 异步加载
define(function(require, exports) {
  require.async('./math', function(math) {
    console.log(math.add(1, 2));
  });
});

特点

  • 依赖就近,延迟执行
  • 用到时才加载依赖

UMD (Universal Module Definition)

兼容 CommonJS、AMD 和全局变量的通用方案。

(function(root, factory) {
  if (typeof define === 'function' && define.amd) {
    // AMD
    define(['dependency'], factory);
  } else if (typeof module === 'object' && module.exports) {
    // CommonJS
    module.exports = factory(require('dependency'));
  } else {
    // 浏览器全局变量
    root.MyModule = factory(root.Dependency);
  }
})(typeof self !== 'undefined' ? self : this, function(dependency) {
  // 模块代码
  return {
    add: function(a, b) { return a + b; }
  };
});

特点

  • 兼容多种环境
  • 常用于打包库文件

ESM (ES Modules)

ES6 原生模块规范,现代浏览器和 Node.js 都支持。

// math.js - 命名导出
export const add = (a, b) => a + b;
export const subtract = (a, b) => a - b;

// 默认导出
export default function multiply(a, b) {
  return a * b;
}

// main.js - 导入
import multiply, { add, subtract } from './math.js';
import * as math from './math.js';

console.log(add(1, 2));        // 3
console.log(multiply(2, 3));   // 6
console.log(math.subtract(5, 2)); // 3

// 动态导入
const module = await import('./math.js');

// HTML 中使用
// <script type="module" src="main.js"></script>

特点

  • 静态分析,编译时确定依赖
  • 导出的是值的引用(绑定)
  • 支持 Tree Shaking

对比表格

规范加载方式运行环境依赖处理
CommonJS同步Node.js运行时加载
AMD异步浏览器依赖前置
CMD异步浏览器依赖就近
UMD-通用兼容多种
ESM异步通用静态分析

关键点

  • CommonJS 同步加载,导出值的拷贝,适合 Node.js
  • AMD 异步加载、依赖前置,RequireJS 实现
  • CMD 异步加载、依赖就近,SeaJS 实现
  • UMD 兼容方案,用于发布通用库
  • ESM 是标准规范,静态分析,支持 Tree Shaking,导出值的引用