在 JavaScript 中,模块化是指将代码拆分成独立的、可复用的片段(模块),每个模块拥有自己的作用域和依赖。随着 JavaScript 生态的不断发展,涌现了多个模块规范。下面我们将全面理解五大主流模块标准:AMD、CMD、CommonJS、UMD 和 ESM。
一、CommonJS(同步加载,主要用于服务端)
特点:
- 模块通过
require()
引入 - 模块通过
module.exports
或exports
暴露接口 - 同步加载模块,适用于服务器端(如 Node.js)
示例:
// math.js
exports.add = function(a, b) {
return a + b;
};
// main.js
const math = require('./math');
console.log(math.add(2, 3)); // 输出:5
优点:
- 简洁、直观
- 服务器端同步加载效率高
缺点:
- 不适合浏览器:浏览器无法同步加载远程模块文件
二、AMD(Asynchronous Module Definition,异步加载,浏览器端)
特点:
- 异步加载模块
- 使用
define()
定义模块,使用require()
加载模块 - 适合浏览器环境
示例(RequireJS 框架):
// 定义模块
define('math', [], function() {
return {
add: function(a, b) {
return a + b;
}
};
});
// 使用模块
require(['math'], function(math) {
console.log(math.add(2, 3));
});
优点:
- 异步加载,适合浏览器
- 支持依赖预加载
缺点:
- 语法冗长,模块定义复杂
三、CMD(Common Module Definition,浏览器端模块标准,由 SeaJS 推广)
特点:
- 异步加载模块
- 推崇依赖就近:只有用到时才
require
- 使用
define()
定义模块
示例(SeaJS):
文件结构:
project/
├── main.js
├── math.js
math.js
:
define(function(require, exports, module) {
exports.add = function(a, b) {
return a + b;
};
exports.sub = function(a, b) {
return a - b;
};
});
main.js
:
define(function(require) {
var math = require('./math');
console.log(math.add(2, 3)); // 输出:5
console.log(math.sub(5, 2)); // 输出:3
});
HTML 调用方式:
<script src="sea.js"></script>
<script>
seajs.use('./main');
</script>
优点:
- 加载更灵活,按需加载
- 适合浏览器端,尤其是前期构建工具不成熟时
缺点:
- 不适用于服务端
- 标准未被广泛采纳,已逐渐被淘汰
四、UMD(Universal Module Definition,统一模块定义)
特点:
- 兼容 CommonJS、AMD、全局变量方式
- 用于编写可以运行在多种环境的模块(Node.js、浏览器等)
示例:
文件结构:
project/
├── myMath.js
├── index.html
myMath.js
:
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD
define([], factory);
} else if (typeof exports === 'object') {
// CommonJS
module.exports = factory();
} else {
// 浏览器全局变量
root.myMath = factory();
}
}(this, function () {
return {
add: function(a, b) {
return a + b;
},
sub: function(a, b) {
return a - b;
}
};
}));
浏览器调用示例:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>UMD Example</title>
</head>
<body>
<script src="myMath.js"></script>
<script>
console.log(myMath.add(4, 2)); // 输出:6
console.log(myMath.sub(5, 3)); // 输出:2
</script>
</body>
</html>
Node.js 调用方式:
const myMath = require('./myMath');
console.log(myMath.add(1, 2)); // 输出:3
RequireJS (AMD) 调用方式:
require(['myMath'], function(myMath) {
console.log(myMath.add(2, 2)); // 输出:4
});
优点:
- 一套代码可同时运行于多种模块系统
- 常用于类库封装
缺点:
- 写法复杂
五、ESM(ECMAScript Modules,ES6 原生模块化标准)
特点:
- 使用
import
和export
语法 - 静态分析,编译时就能确定模块依赖关系
- 浏览器和现代 Node.js 都已支持
示例:
// math.js
export function add(a, b) {
return a + b;
}
// main.js
import { add } from './math.js';
console.log(add(2, 3));
优点:
- 语法标准,未来趋势
- 原生支持,支持 Tree-Shaking(优化打包)
缺点:
- 浏览器端必须使用
type="module"
并遵守 CORS 同源策略 - Node.js 需使用
.mjs
扩展名或设置"type": "module"
才支持
总结:
- 前端现代项目(Vue, React):使用 ESM
- Node.js 项目:推荐使用 CommonJS(ESM 也越来越常见)
- 兼容库开发:采用 UMD
- 旧项目或兼容老浏览器:可能还会用到 AMD/CMD,但已逐渐淘汰
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。