在 JavaScript 中,模块化是指将代码拆分成独立的、可复用的片段(模块),每个模块拥有自己的作用域和依赖。随着 JavaScript 生态的不断发展,涌现了多个模块规范。下面我们将全面理解五大主流模块标准:AMD、CMD、CommonJS、UMD 和 ESM


一、CommonJS(同步加载,主要用于服务端)

特点:

  • 模块通过 require() 引入
  • 模块通过 module.exportsexports 暴露接口
  • 同步加载模块,适用于服务器端(如 Node.js)

示例:

// math.js
exports.add = function(a, b) {
  return a + b;
};

// main.js
const math = require('./math');
console.log(math.add(2, 3)); // 输出:5

优点:

  • 简洁、直观
  • 服务器端同步加载效率高

缺点:

  • 不适合浏览器:浏览器无法同步加载远程模块文件

二、AMD(Asynchronous Module Definition,异步加载,浏览器端)

特点:

  • 异步加载模块
  • 使用 define() 定义模块,使用 require() 加载模块
  • 适合浏览器环境

示例(RequireJS 框架):

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

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

优点:

  • 异步加载,适合浏览器
  • 支持依赖预加载

缺点:

  • 语法冗长,模块定义复杂

三、CMD(Common Module Definition,浏览器端模块标准,由 SeaJS 推广)

特点:

  • 异步加载模块
  • 推崇依赖就近:只有用到时才 require
  • 使用 define() 定义模块

示例(SeaJS):

文件结构:

project/
├── main.js
├── math.js

math.js

define(function(require, exports, module) {
  exports.add = function(a, b) {
    return a + b;
  };

  exports.sub = function(a, b) {
    return a - b;
  };
});

main.js

define(function(require) {
  var math = require('./math');
  console.log(math.add(2, 3)); // 输出:5
  console.log(math.sub(5, 2)); // 输出:3
});

HTML 调用方式:

<script src="sea.js"></script>
<script>
  seajs.use('./main');
</script>

优点:

  • 加载更灵活,按需加载
  • 适合浏览器端,尤其是前期构建工具不成熟时

缺点:

  • 不适用于服务端
  • 标准未被广泛采纳,已逐渐被淘汰

四、UMD(Universal Module Definition,统一模块定义)

特点:

  • 兼容 CommonJS、AMD、全局变量方式
  • 用于编写可以运行在多种环境的模块(Node.js、浏览器等)

示例:

文件结构:

project/
├── myMath.js
├── index.html

myMath.js

(function (root, factory) {
  if (typeof define === 'function' && define.amd) {
    // AMD
    define([], factory);
  } else if (typeof exports === 'object') {
    // CommonJS
    module.exports = factory();
  } else {
    // 浏览器全局变量
    root.myMath = factory();
  }
}(this, function () {
  return {
    add: function(a, b) {
      return a + b;
    },
    sub: function(a, b) {
      return a - b;
    }
  };
}));

浏览器调用示例:

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>UMD Example</title>
</head>
<body>
  <script src="myMath.js"></script>
  <script>
    console.log(myMath.add(4, 2)); // 输出:6
    console.log(myMath.sub(5, 3)); // 输出:2
  </script>
</body>
</html>

Node.js 调用方式:

const myMath = require('./myMath');
console.log(myMath.add(1, 2)); // 输出:3

RequireJS (AMD) 调用方式:

require(['myMath'], function(myMath) {
  console.log(myMath.add(2, 2)); // 输出:4
});

优点:

  • 一套代码可同时运行于多种模块系统
  • 常用于类库封装

缺点:

  • 写法复杂

五、ESM(ECMAScript Modules,ES6 原生模块化标准)

特点:

  • 使用 importexport 语法
  • 静态分析,编译时就能确定模块依赖关系
  • 浏览器和现代 Node.js 都已支持

示例:

// math.js
export function add(a, b) {
  return a + b;
}

// main.js
import { add } from './math.js';
console.log(add(2, 3));

优点:

  • 语法标准,未来趋势
  • 原生支持,支持 Tree-Shaking(优化打包)

缺点:

  • 浏览器端必须使用 type="module" 并遵守 CORS 同源策略
  • Node.js 需使用 .mjs 扩展名或设置 "type": "module" 才支持

总结:

  • 前端现代项目(Vue, React):使用 ESM
  • Node.js 项目:推荐使用 CommonJS(ESM 也越来越常见)
  • 兼容库开发:采用 UMD
  • 旧项目或兼容老浏览器:可能还会用到 AMD/CMD,但已逐渐淘汰

fuGUI
1.8k 声望2.8k 粉丝

The best time to plant a tree is ten years ago, and the second,let us start