【导入模式】AMD & ESM & CJS & UMD区别

突然有点模糊了,故浅总结下这几个导入模式的区别😂

一、核心特性对比表

特性 CommonJS (CJS) AMD ESM (ES Modules) UMD
加载方式 同步 (Node.js) 异步 (浏览器优先) 静态/动态 (原生支持) 环境自适应
语法 require/module.exports define/require import/export IIFE 包裹多种环境判断
作用域 模块作用域 函数作用域 模块作用域 全局变量或模块作用域
适用环境 Node.js 服务端 浏览器 (RequireJS) 现代浏览器/Node.js 14+ 跨环境兼容
Tree-Shaking 困难 困难 原生支持 困难
典型应用 npm 包开发 旧浏览器项目 现代前端框架 通用库开发

二、CommonJS (CJS)

ini 复制代码
javascript
// 导出
module.exports = { key: 'value' };
exports.fn = () => {};

// 导入
const lib = require('./lib');
const { fn } = require('./utils');

技术原理

  • 设计于 2009 年,Node.js 默认模块系统
  • 同步加载导致浏览器端性能问题
  • 模块缓存机制 (require.cache)

现代应用

  • 仍主导 Node.js 生态 (85%+ npm 包使用 CJS)
  • 需配合 @babel/plugin-transform-modules-commonjs 转换 ESM

三、AMD (Asynchronous Module Definition)

javascript 复制代码
javascript
// 定义模块
define('moduleId', ['dep1', 'dep2'], (d1, d2) => {
  return { method: () => d1.action() };
});

// 加载模块
require(['module'], (mod) => mod.method());

技术原理

  • RequireJS 实现的浏览器优先方案
  • 依赖前置声明 + 异步加载
  • 适合网络环境差的旧浏览器场景

现代替代

  • 逐步被 <script type="module"> 原生 ESM 替代
  • Webpack 等工具可输出 AMD 格式供旧系统使用

四、ESM (ECMAScript Modules)

javascript 复制代码
javascript
// 导出
export const name = 'ESM';
export default function() {};

// 导入
import { name } from './module.js';
import lib from './lib.js';

核心优势

  1. 静态结构:编译时确定依赖关系,支持 Tree-Shaking
  2. 浏览器原生支持:无需打包工具直接运行 (Chrome 61+ / Firefox 60+)
  3. 循环引用处理:通过实时绑定 (Live Binding) 安全处理
  4. 动态导入import() 实现代码分割

Node.js 集成

json 复制代码
json
// package.json
{
  "type": "module",  // 启用 ESM
  "exports": {
    ".": {
      "import": "./esm/index.js",  // ESM 入口
      "require": "./cjs/index.cjs" // CJS 回退
    }
  }
}

五、UMD (Universal Module Definition)

javascript 复制代码
javascript
(function (root, factory) {
  if (typeof define === 'function' && define.amd) {
    // AMD
    define(['dep'], factory);
  } else if (typeof exports === 'object') {
    // CJS
    module.exports = factory(require('dep'));
  } else {
    // 全局
    root.MyLib = factory(root.Dep);
  }
})(this, (dep) => {
  // 模块逻辑
  return { /* ... */ };
});

设计哲学

  • 兼容层模式:一套代码适配 AMD/CJS/全局变量
  • 文件体积通常比单环境格式大 30%+ (含环境检测代码)

现代应用场景

  • 需通过 <script> 标签直接引入的公共库 (如图表库)
  • 微前端架构中的跨技术栈模块共享

六、模块转换关系图

graph LR A(CJS) -->|Babel/ESBuild| D(ESM) B(AMD) -->|Webpack| D D -->|Rollup| C(UMD) C -->|Tree-Shaking| D

七、工具链支持对比

工具 CJS 支持 AMD 支持 ESM 支持 UMD 支持
Webpack ✅ 原生 ✅ 插件 ✅ 5.0+ 原生 ✅ 配置输出格式
Rollup ✅ 插件 ✅ 插件 ✅ 原生 ✅ 配置输出格式
Vite ✅ 预构建转换 ❌ 需插件 ✅ 原生 ✅ 构建输出选项
ESBuild ✅ 自动转换 ✅ 原生 ✅ 格式选项

八、选型建议指南

  1. 新项目开发

    • 前端项目 → 原生 ESM + Vite
    • Node.js 服务 → 混合 ESM/CJS (逐步迁移)
  2. 库开发

    • 公共库 → UMD + ESM 双格式发布
    • 私有库 → ESM 单格式
  3. 遗留系统维护

    • 浏览器端 → AMD → 用 Webpack 转换为 ESM
    • Node.js → CJS → 增量迁移为 ESM
  4. 性能关键型

    • 浏览器端 → ESM 原生加载 + HTTP/2
    • 服务端 → CJS (同步加载无性能瓶颈)

九、代码示例:现代库的多格式发布

项目结构

perl 复制代码
dist/
  ├── my-lib.umd.js     # UMD 格式
  ├── my-lib.esm.js     # ESM 格式
  └── my-lib.cjs.js     # CJS 格式

package.json 配置

json 复制代码
json
{
  "name": "my-lib",
  "main": "dist/my-lib.cjs.js",
  "module": "dist/my-lib.esm.js",
  "browser": "dist/my-lib.umd.js",
  "exports": {
    ".": {
      "import": "./dist/my-lib.esm.js",
      "require": "./dist/my-lib.cjs.js"
    }
  }
}
相关推荐
Nan_Shu_6148 分钟前
Web前端面试题(2)
前端
知识分享小能手15 分钟前
React学习教程,从入门到精通,React 组件核心语法知识点详解(类组件体系)(19)
前端·javascript·vue.js·学习·react.js·react·anti-design-vue
蚂蚁RichLab前端团队1 小时前
🚀🚀🚀 RichLab - 花呗前端团队招贤纳士 - 【转岗/内推/社招】
前端·javascript·人工智能
孩子 你要相信光1 小时前
css之一个元素可以同时应用多个动画效果
前端·css
huangql5202 小时前
npm 发布流程——从创建组件到发布到 npm 仓库
前端·npm·node.js
Days20502 小时前
LeaferJS好用的 Canvas 引擎
前端·开源
小白菜学前端2 小时前
vue2 常用内置指令总结
前端·vue.js
林_深时见鹿2 小时前
Vue + ElementPlus 自定义指令控制输入框只可以输入数字
前端·javascript·vue.js
椒盐螺丝钉2 小时前
Vue组件化开发介绍
前端·javascript·vue.js
koooo~2 小时前
v-model与-sync的演变和融合
前端·javascript·vue.js