突然有点模糊了,故浅总结下这几个导入模式的区别😂
一、核心特性对比表
特性 | 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';
核心优势:
- 静态结构:编译时确定依赖关系,支持 Tree-Shaking
- 浏览器原生支持:无需打包工具直接运行 (Chrome 61+ / Firefox 60+)
- 循环引用处理:通过实时绑定 (Live Binding) 安全处理
- 动态导入 :
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 | ✅ 自动转换 | ❌ | ✅ 原生 | ✅ 格式选项 |
八、选型建议指南
-
新项目开发:
- 前端项目 → 原生 ESM + Vite
- Node.js 服务 → 混合 ESM/CJS (逐步迁移)
-
库开发:
- 公共库 → UMD + ESM 双格式发布
- 私有库 → ESM 单格式
-
遗留系统维护:
- 浏览器端 → AMD → 用 Webpack 转换为 ESM
- Node.js → CJS → 增量迁移为 ESM
-
性能关键型:
- 浏览器端 → 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"
}
}
}