概述
历史上,JavaScript 一直没有模块(module)体系,只用一个文件来写程序代码,对于开发维护大型的、复杂的项目是极不友好的。
后来,在社区相继推出了 commonJS、AMD、CMD、UMD、ES module 等模块。
模块介绍
CommonJS
推出时间大约在 2009 年。Node.js 选择了 CommonJS 作为其模块系统,从而使其在服务器端得以广泛应用。它的特性是同步加载模块,在服务器端模块文件都存放在本地磁盘,读取非常快,所以这样做不会有问题。但是,在浏览器端,要从服务器端加载模块,若采用同步加载方式会导致性能、体验等问题
javascript
// math.js文件:自定义方法
function add(a, b) {
return a + b;
}
module.exports = {
add: add,
};
// index.js文件:引用自定义的模块
var math = require("./math");
math.add(1, 2);
AMD(Asynchronous Module Definition)
推出时间大约在 2011 年。其主要应用场景是浏览器端,异步加载模块,与 CommonJS 最大的不同在于,AMD 采用异步方式加载模块,模块的加载不影响它后面语句的运行。所有依赖某些模块的语句均放置在回调函数中。知名的库有 require.js
javascript
// 导入模块
require(["module1", "module2"], function (module1, module2) {
//使用导入的模块
});
// 定义模块
define(["dependency"], function (dependency) {
return moduleName;
});
CMD(Common Module Definition)
推出时间大约在 2012 年。同样是为了解决浏览器端的模块化问题,CMD 相比 AMD 使用的是依赖就近,延迟执行等特性,写代码的时候哪里使用哪里再引入模块。知名的库有 sea.js
javascript
// 定义导出模块 math.js
define(function (require, exports, module) {
var $ = require("jquery.js");
var add = function (a, b) {
return a + b;
};
exports.add = add;
});
// 在需要使用的地方就近导入模块
seajs.use(["math.js"], function (math) {
var sum = math.add(1 + 2);
});
UMD(Universal Module Definition)
推出时间大约在 2013 年。UMD 主要是解决跨平台模块化方案的问题,它合并了 CommonJS 和 AMD 规范,能够兼容各种情况的环境,同时运行在客户端和服务器端,因此被称为是通用的模块定义。当使用 Rollup/Webpack 之类的打包器时,UMD 通常用作备用模块
javascript
(function (root, factory) {
if (typeof define === "function" && define.amd) {
define(["dependency"], factory);
} else if (typeof exports === "object") {
module.exports = factory(require("dependency"));
} else {
root.returnExports = factory(root.dependency);
}
})(this, function (dependency) {
return {}; //返回值即为定义的模块
});
ES module
推出时间为 2015 年。ES6 或更高版本在语言标准层面上实现了模块功能,解决了 JavaScript 文件无法使用import
, export
命令,在语法层面上对模块进行了支持,标准化产品,使得 JavaScript 模块化具备了规范性。可以直接运行在现代浏览器中,也可以作为服务器端模块使用
javascript
// 导出模块
export default SomeObject;
// 导入模块
import moduleName from "./module";
优缺点对比
模块 | 优点 | 缺点 |
---|---|---|
CommonJS | 服务器端模块,同步加载,写法简单直观;Node.js选择了CommonJS作为其模块系统,使其在服务端得以广泛应用。 | 无法直接在浏览器中运行,需通过如Browserify的工具转换;同步加载可能导致性能问题,特别是处理大型、复杂的依赖树时。 |
AMD | 解决了浏览器环境下模块的异步加载问题,特别在处理大型、复杂的依赖树时有较好性能;可以并行加载多个模块。 | 代码的阅读和书写比较困难 |
CMD | CMD规范和AMD规范类似,但是依赖就近,延迟执行,只需要简单的define函数。 | CMD规范和AMD规范类似,非官方标准,需要运行在如Sea.js之类的运行库上。 |
UMD | UMD模块格式被设计成可以在CommonJS和AMD两个最常见的JavaScript模块定义格式之间进行切换;能够在几乎所有的JavaScript运行环境下执行。 | UMD的代码结构略显复杂 |
ES Module | ES Module是JavaScript官方标准,出色的静态模块结构使得编译时就能确定模块的依赖关系,也有助于静态分析和Tree Shaking等优化;浏览器原生支持,无需引入库或者构建工具进行转换,使用简便。 | 一些旧版浏览器并不支持ES Module。 |
总结
- 由于ES module的设计思想是尽量的静态化,使得代码可以静态分析和tree shaking等优化,是目前最好的模块化方案
- UMD 用得很广泛,通常在 ESM 不起作用的情况下用作备用
- CJS 是同步的,适合服务端
- AMD/CMD 是异步的,适合用户端