在 JavaScript 的发展过程中,模块化解决方案不断演进,涌现了多种模块格式。本文将系统介绍最常用的几种:CommonJS、AMD、UMD 和 ESM,并穿插介绍 IIFE(Immediately Invoked Function Expression)作为背景知识,以便理解它们的使用场景和差异。
一、CommonJS
1.1 介绍
CommonJS 是最早专门为 Node.js 设计的模块系统。它通过 require()
引入模块,使用 module.exports
或 exports
导出模块。模块是同步加载的,因此更适合服务器端。
1.2 适用场景
- Node.js 后端项目。
- 服务器端环境,模块加载速度快,不依赖异步。
1.3 使用方法
// math.js
module.exports = {
add(a, b) {
return a + b;
}
};
// main.js
const math = require('./math');
console.log(math.add(2, 3)); // 输出:5
1.4 特点总结
- 适合服务器端同步加载。
- 在浏览器环境需要打包工具(如 Webpack、Browserify)处理。
- 加载方式简单直观。
二、AMD(Asynchronous Module Definition)
2.1 介绍
AMD 规范是为了解决浏览器端模块的 异步加载 问题提出的。代表实现是 RequireJS。它通过 define()
函数定义模块及其依赖,模块可以在需要时动态加载,提高前端性能。
2.2 适用场景
- 浏览器端项目。
- 需要异步按需加载模块的大型应用。
2.3 使用方法
// 定义模块 math.js
define([], function() {
return {
add(a, b) {
return a + b;
}
};
});
// 引入模块
require(['math'], function(math) {
console.log(math.add(2, 3)); // 输出:5
});
2.4 特点总结
- 适合浏览器环境,模块加载不会阻塞页面。
- 需要模块加载器(如 RequireJS)支持。
- 定义和使用模块语法较冗长。
(插曲)IIFE(Immediately Invoked Function Expression)
在正式介绍 UMD 之前,我们需要了解一下 IIFE,因为它是 UMD 的技术基础之一。
IIFE 介绍
IIFE 是立即执行函数表达式,它在定义后立刻执行,常用于创建私有作用域,避免变量污染全局环境。
使用示例
(function() {
console.log('This function runs immediately!');
})();
为什么重要?
UMD 和其他模块打包格式常常利用 IIFE 包裹代码,以便:
- 在不同环境下自动检测并执行相应逻辑。
- 避免污染全局作用域。
三、UMD(Universal Module Definition)
3.1 介绍
UMD 是一种综合方案,它能让同一段代码同时兼容 CommonJS、AMD 和浏览器全局变量。通常使用 IIFE 检测当前环境,并根据环境选择不同的导出方式。
3.2 适用场景
- 开发需要同时在 Node.js、AMD、浏览器 使用的库。
- 保证最大的兼容性,适配不同的模块系统。
3.3 使用方法
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD
define([], factory);
} else if (typeof module === 'object' && module.exports) {
// CommonJS
module.exports = factory();
} else {
// 浏览器全局
root.MyLib = factory();
}
}(this, function () {
return {
hello() {
return 'Hello, UMD!';
}
};
}));
3.4 特点总结
- 超强兼容性:Node.js、AMD、浏览器。
- 适合发布公共库、SDK 等。
四、ESM(ECMAScript Modules)
4.1 介绍
ESM 是 ECMAScript 官方在 ES6(2015)标准中引入的模块系统,原生支持 import
和 export
。目前已经被现代浏览器和 Node.js 广泛支持。
4.2 适用场景
- 现代前端开发(React、Vue、Svelte 等框架)。
-
现代 Node.js 项目(v12+,默认或通过
.mjs
后缀使用)。 - 支持 tree-shaking 优化打包体积。
4.3 使用方法
// math.js
export function add(a, b) {
return a + b;
}
// main.js
import { add } from './math.js';
console.log(add(2, 3)); // 输出:5
<html>
<head>
<title>remote component test</title>
</head>
<body>
<div id="app">
<helloWorld :msg="message"></helloWorld>
</div>
<!-- import script vue3 -->
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script type="module">
const { createApp, defineAsyncComponent } = Vue
import helloworld from 'http://wd-local.com/HelloWorld.es.js'
// const helloworld = defineAsyncComponent(() =>
// import('http://wd-local.com/HelloWorld.es.js')
// )
createApp({
components: {
helloworld
},
data () {
return {
message: 'static web page'
}
}
}).mount('#app')
</script>
</body>
</html>
image.png
4.4 特点总结
- 静态结构,能被编译器提前分析。
- 支持按需加载、tree-shaking。
- 是未来的标准,推荐新项目默认使用。
五、各种模块格式对比
格式 | 语法 | 支持环境 | 加载方式 | 异步加载 | 特点 |
---|---|---|---|---|---|
CommonJS |
require / module.exports
|
Node.js | 同步 | 否 | 简单直观,服务器端标准 |
AMD | define |
浏览器 (RequireJS) | 异步 | 是 | 浏览器异步模块加载 |
UMD | IIFE 检测 + define / module.exports
|
浏览器、Node.js | 同步或异步 | 支持 | 兼容多环境,适合发布库 |
ESM |
import / export
|
现代浏览器、Node.js | 异步(默认) | 是 | 静态分析、tree-shaking 优化 |
六、总结与建议
使用场景 | 推荐模块格式 |
---|---|
纯后端项目(Node.js) | CommonJS |
前端老项目(依赖 RequireJS 等) | AMD |
发布通用库,兼容多个环境 | UMD |
新的前端项目、现代化 Node.js 项目 | ESM |
小提示:
- CommonJS 简单好用,但已不适合新前端项目(vite默认不支持)。
- AMD 渐渐式微,主要历史项目中出现。
- UMD 适合写一次到处运行的通用 SDK。
- ESM 是未来发展趋势,浏览器和 Node.js 都在全面支持!