一篇搞定CJS,AMD,CMD,ESM

221 阅读3分钟

不知道有没有被缩写搞头疼过,为什么明明直接说全程就能明白的名词就要搞缩写。。。

以下是各模块化规范的对比分析,一张表直接理解:

特性CommonJSAMD (RequireJS)CMD (Sea.js)ES6 Modules
加载方式同步加载(服务器端)异步加载(浏览器端)异步加载(浏览器端)静态加载(编译时确定依赖)
语法require / module.exportsdefine / requiredefine / requireimport / export
依赖声明时机动态依赖(运行时解析)依赖前置(提前声明)依赖就近(按需声明)静态依赖(编译时解析)
模块执行时机加载时执行提前执行(依赖前置)延迟执行(按需执行)编译时加载,只执行一次
输出类型值的拷贝(原始类型不受影响)值的拷贝值的拷贝值的引用(实时绑定)
循环依赖处理支持但可能导致部分加载支持但需谨慎管理支持但需谨慎管理静态分析支持更好
适用环境Node.js浏览器端浏览器端浏览器 + Node.js(现代环境)
优缺点✅ 简单易用
❌ 同步加载不适用浏览器
✅ 异步加载优化性能
❌ 依赖前置不够灵活
✅ 依赖就近更灵活
❌ 社区生态较小
✅ 语言标准、静态优化
❌ 旧环境需转译

详细解释及代码示例

1. CommonJS

  • 场景:Node.js 的默认模块系统。
  • 特点
    • 同步加载模块,模块在首次 require 时加载并执行。
    • 输出的是值的拷贝(原始类型为拷贝,对象为引用)。
    // math.js
    let counter = 0;
    const add = () => counter++;
    module.exports = { counter, add };
    
    // main.js
    const { counter, add } = require('./math');
    add();
    console.log(counter); // 输出 0(counter 是拷贝值)
    

2. AMD (Asynchronous Module Definition)

  • 场景:浏览器端异步加载(如 RequireJS)。
  • 特点
    • 依赖前置,模块加载后立即执行。
    // 定义模块
    define(['dep1', 'dep2'], function(dep1, dep2) {
      return { method: () => dep1.doSomething() };
    });
    
    // 使用模块
    require(['moduleA'], function(moduleA) {
      moduleA.method();
    });
    

3. CMD (Common Module Definition)

  • 场景:浏览器端(如 Sea.js),强调按需加载。
  • 特点
    • 依赖就近,模块使用时才加载。
    define(function(require, exports, module) {
      const dep1 = require('dep1'); // 按需加载
      exports.method = () => dep1.doSomething();
    });
    

4. ES6 Modules

  • 场景:现代浏览器和 Node.js(需配置)。
  • 特点
    • 静态分析,编译时确定依赖。
    • 输出值的引用(实时绑定)。
    // math.js
    export let counter = 0;
    export const add = () => counter++;
    
    // main.js
    import { counter, add } from './math.js';
    add();
    console.log(counter); // 输出 1(counter 是引用)
    

再废话一次,核心区别总结

  1. 加载机制

    • CommonJS 同步加载,适合服务器。
    • AMD/CMD 异步加载,适合浏览器。
    • ES6 静态加载,通用且支持优化。
  2. 模块定义与依赖管理

    • CommonJS/AMD/CMD 动态依赖,ES6 静态依赖。
    • AMD 依赖前置,CMD 依赖就近,ES6 依赖声明在顶部。
  3. 值的传递

    • CommonJS/AMD/CMD 输出值拷贝,ES6 输出实时引用。
  4. 生态与未来

    • ES6 是语言标准,支持 Tree-shaking 等优化。
    • CommonJS 主导 Node.js 生态,AMD/CMD 逐渐被替代。

在实际应用中建议大家

  • 现代项目:优先使用 ES6 Modules,结合 Webpack/Rollup 打包兼容旧环境。
  • Node.js 开发:CommonJS 为主,逐步迁移到 ES6。
  • 遗留浏览器项目:AMD 或 CMD 过渡,最终转向 ES6 + 打包工具。