梳理一下前端模块化规范:CommonJS ESM AMD CMD UMD

前端模块化规范在发展过程中出现过多种规范,大多开发者都对这些名词有个印象,但问起来又有些模糊。本文的目的是做一个梳理,帮助记忆。

先上一张对比表:

类型 核心定位 语法关键词 适用环境 特点
CommonJS(CJS) Node.js 默认模块规范 require、module.exports、exports Node.js 服务端、Webpack 打包 同步加载、运行时加载、浅拷贝
ESM 官方标准模块规范 import、export、export default 现代浏览器、Node.js v14+ 异步 / 静态加载、编译时确定依赖、绑定引用
AMD 异步模块规范 define、require 旧版前端项目(RequireJS) 依赖前置、异步加载、不阻塞页面
CMD 异步模块规范 define、require 旧版前端项目(SeaJS) 依赖就近、按需加载、风格接近 Node
UMD 兼容模块格式 自执行函数包裹 浏览器全局、AMD、CommonJS 自动判断环境、一套代码多环境可用
Dual Package 双产物发包策略 打出 2 个包 npm 库、多环境兼容项目 同时输出 CJS 和 ESM 产物,工具自动识别

一、CommonJS(CJS)

CommonJS 是 Node.js 的默认模块化规范,也是早期前端打包支持的规范,很多老 Node 项目至今还在使用。

javascript 复制代码
// 导出模块(a.js)
// 方式1:整体导出
module.exports = {
  add: (a, b) => a + b,
  name: "CommonJS模块"
};
// 方式2:单个导出
exports.sub = (a, b) => a - b;

// 导入模块(b.js)
const mod = require('./a.js');
console.log(mod.add(1, 2)); // 3
console.log(mod.sub(3, 1)); // 2

特点:

  1. 同步加载:在 Node.js 服务端,文件都在本地,同步加载不会有问题。

  2. 浅拷贝:导出的是对象浅拷贝,后续原模块修改该值,导入方拿到的副本不会变。

补充:CommonJS 没有官方缩写,但行业内普遍简称 CJS。

二、ESM ------ 官方标准

ESM(ES Modules)是 ECMAScript 官方推出的模块化规范,也是目前前端开发的主流 ------ 现代浏览器、Node.js(v14+)、Vue/React 等框架,全部默认支持 ESM。

它解决了 CommonJS 的诸多痛点,比如同步加载、不支持 Tree-Shaking 等,语法也更简洁。

javascript 复制代码
// 导出模块(a.js)
// 方式1:命名导出(可多个)
export const add = (a, b) => a + b;
export const name = "ESM模块";
// 方式2:默认导出(仅1个)
export default function sub(a, b) {
  return a - b;
}

// 导入模块(b.js)
import { add, name } from './a.js';
import sub from './a.js';

console.log(add(1, 2)); // 3
console.log(sub(3, 1)); // 2

特点:

  1. 静态编译:编译时就确定依赖关系,支持 Tree-Shaking(删除无用代码),打包体积更小;浏览器中是异步加载,不阻塞页面。

  2. 绑定引用:导出的是值的引用,原模块修改该值,导入方也会同步变化(和 CommonJS 的 "浅拷贝" 区分)。

三、AMD + CMD

  • AMD(Asynchronous Module Definition),代表工具是 RequireJS,核心是依赖前置------ 一开始就声明所有依赖,异步加载完成后执行回调。
  • CMD(Common Module Definition),代表工具是 SeaJS,核心是依赖就近------ 用到某个模块时,再去 require,写法风格接近 Node 的 CommonJS。
  • AMD 是依赖前置、提前加载;CMD 是依赖就近、懒执行,两者现在都已淘汰,不必深入学习。

四、UMD

UMD(Universal Module Definition),直译是 "通用模块定义",它不是一种新的规范,而是一种兼容方案------ 一套代码,能同时适配 CommonJS、AMD、浏览器全局变量。自动判断当前运行环境,选择对应的模块化方式。

标准无依赖 UMD 写法:

javascript 复制代码
(function (root, factory) {
  // AMD
  if (typeof define === 'function' && define.amd) {
    define(factory);
  }
  // CommonJS
  else if (typeof module === 'object' && module.exports) {
    module.exports = factory();
  }
  // 浏览器全局
  else {
    root.MyModule = factory();
  }
})(this, function () {
  // 模块逻辑
  return {
    add: (a, b) => a + b
  };
});

特点:万能兼容,一套打包产物能在任何环境运行;但缺点是代码冗余,现在的开源库已经很少用 UMD,转而用更简洁的 Dual Package。

五、Dual Package ------ 双产物包

它是一种发包策略,同时打包输出 CJS 和 ESM 两种产物,让项目既能支持 CommonJS,也能支持 ESM。

现代 npm 库的标准配置:

javascript 复制代码
{
  "name": "my-utils",
  "main": "dist/index.cjs",
  "module": "dist/index.mjs",
  "exports": {
    ".": {
      "import": "./dist/index.mjs",
      "require": "./dist/index.cjs"
    }
  }
}

优势:不用写环境判断代码,比 UMD 更简洁;适配所有现代项目和老 Node 项目,是目前开源库的主流发包方式。

六、package.json 的 type 字段

type 是 Node.js 官方字段,用于声明模块化格式,影响 .js 文件的解析方式。

  • "type": "module":按 ESM 解析

  • 不配置:默认按 CommonJS 解析

七、Webpack 打包指定输出规范

Webpack 5 推荐使用 output.library.type

输出 CommonJS:

javascript 复制代码
module.exports = {
  output: {
    filename: 'bundle.js',
    library: { type: 'commonjs2' }
  }
};

输出 ESM:

javascript 复制代码
module.exports = {
  output: {
    filename: 'bundle.mjs',
    library: { type: 'module' }
  },
  experiments: { outputModule: true }
};

输出 UMD:

javascript 复制代码
module.exports = {
  output: {
    library: { type: 'umd' }
  }
};

输出双产物:分别用两份配置打包两次即可。

总结

  1. 前端项目(Vue/React/Vite/Webpack):一律用 ESM。

  2. Node.js 项目:老项目用 CommonJS,新项目推荐 ESM。

  3. 开发 npm 库:使用 Dual Package 双产物;极老环境可额外打包 UMD。

相关推荐
OpenTiny社区5 分钟前
Coding Agent 到底是怎么工作的,它又为什么会在复杂代码库里失真?
前端·github·agent
奇奇怪怪的10 分钟前
Graph RAG:知识图谱增强检索
前端
默_笙20 分钟前
🌏 MCP 是 AI 界的 USB-C:一个协议让所有模型都能用上所有工具
前端·javascript
以和为贵33 分钟前
🔥前端也能搞懂流式输出:从 SSE 到打字机效果
前端·人工智能·架构
阿成学长_Cain1 小时前
Linux telinit 命令详解:运行级别切换|关机重启|系统维护一站式掌握
linux·运维·前端·网络
Patrick_Wilson2 小时前
最佳实践是有保质期的:从一次 CDN external 白屏事故说起
前端·性能优化·前端工程化
YHHLAI2 小时前
[特殊字符] Agent 智能体开发实战 · 第一课:Tool Use —— 让大模型自动干活
前端·人工智能
nuIl2 小时前
我把 5 个编码 Agent 塞进了一个 npm 包
前端·agent·claude
L-影2 小时前
FastAPI 静态文件:Web 页面的“固定展柜”与“加速引擎”
前端·fastapi
陪我去看海2 小时前
受够了 AI 写完代码留下一堆端口?我做了一个端口管理App!
前端·macos·vibecoding