原因
javascript早期作为一门轻量级语言,用于在 Web 上与用户进行少量的交互,并没有依赖管理的概念。但随着ajax技术的发展,js被要求实现的功能越来越多。全局变量污染,依赖混乱等问题亟待解决。其他语言已经实现的模块化被提上日程。
前驱
在正式的模块化方案发布之前,有人想了其他方法解决变量污染的问题
- 雅虎的开源组件库 YUI Library
采用了类似java命名空间的方式,但是因为调用和封装都太过复杂并没有推广起来
YUI.util.module.doSomthing();
- JQuery
运用了闭包的特性,在加载之初执行函数,把JQuery变量挂载到全局的window对象上
(function(root){
root.JQuery = root.$ = JQuery
})(window)
正式起航
1、CommonJS -- 最早的解决方案
1.1 背景
- mozilla 的工程师为解决用于服务端的js的自动化测试的模块导入问题,制定了一套模块化导入的规范,命名为ServerJS(Modules/0.1),这就是CommonJS的前身。
- 后来正式更名为CommonJS(Modules/1.0),从命名可以看出其野心,企图一统所有编程语言的模块化方案。
- Node的创始人决定为其包依赖管理寻找解决办法时选择了CommonJS的规范来,我觉得对其的推广起到了极大的促进作用。
1.2 具体实现
- 在CommonJS中一个文件就是一个模块,拥有自己独立的作用域,变量及方法;
- 定义全局变量
require
,通过在require
中传入唯一的标识来引入文件的依赖模块,其执行结果就是模块暴露的API; - 如果执行失败,
require
会抛出一个错误; - 模块通过
exports
向外暴露API,导出的必须是一个object
,暴露的API是该对象的属性
//moudleA.js
moudle.exports = {
a: 1
};
//moudleB.js
var ma = require('./moudleA');
var b = ma.a + 2;
module.exports ={
b: b
};
分裂统一(AMD、CMD、UMD)
由于CommonJS是起源于服务端的,所以虽然说他是一门很好的模块化解决办法的实现方法,但是其并不能运用于浏览器端。所以浏览器端需要新的实现方法。但是在实现方法上CommonJS内部出现了分歧,从而诞生了AMD、CMD、UMD三种解决方法(当然还有其他小众的方法,因为其使用人数较少这里不做讨论)。
保守派 —— browserify
这一派的理念是不动CommonJS,为配合浏览器实现,可以使用一些其他方法将其转化成浏览器可以理解的方法。跟我们现在的Babel
的实现思想是一样的。browserify就是这一观点下的产物。
激进派—— AMD(RequireJS)
这一派的观点是既然不适用了那就改,应该依据浏览器的特点放弃require
,改用回调
方式。但是其观点并没有被CommonJS社区接受所以这一派最终脱离了CommonJS社区自立门户,起初是专注于RequireJS的开发,后来才起草了AMD 异步模块定义规范(Async Module Definition)。
AMD 作为一个规范,只需定义其语法 API,而不关心其实现。AMD 规范简单到只有一个 API,即 define 函数:
define(id?, dependencies?, factory);
- 定义全局函数 define(id, dependencies, factory),用于定义模块。dependencies 为依赖的模块数组,在 factory 中需传入形参与之一一对应。
- 如果 dependencies 的值中有 require、exports 或module,则与 CommonJS 中的实现保持一致。
- 如果 dependencies 省略不写,则默认为 ['require', 'exports', 'module'],factory 中也会默认传入三者。
- 如果 factory 为函数,模块可以通过以下三种方式对外暴漏 API:return 任意类型;exports.XModule = XModule、module.exports = XModule。
- 如果 factory 为对象,则该对象即为模块的导出值。
中间派——CMD、UMD
1. CMD
这一派认为require
不用放弃,但export
也可以导出除object
以外的其他类型。淘宝玉伯写出了SeaJS,提出了CMD(Common Module Definition)规范,其在国内很火,但并未在国外流行。
CMD 规范的主要内容与 AMD 大致相同,不过保留了 CommonJS 中最重要的延迟加载、就近声明(就近依赖)特性。
// moudleA.js
define(function(require, exports, module) {
module.exports = {
a: 1
};
});
// moudleB.js
define(function(require, exports, module) {
var ma = require('./moudleB');
var b = ma.a + 2;
module.exports = {
b: b
};
});
2. UMD
UMD 即 Universal Module Definition 的缩写,它本质上并不是一个真正的模块化方案,而是将 CommonJS 和 AMD 相结合。
((root, factory) => {
if (typeof define === 'function' && define.amd) {
//AMD
define(['jquery'], factory);
} else if (typeof exports === 'object') {
//CommonJS
var $ = requie('jquery');
module.exports = factory($);
} else {
root.testModule = factory(root.jQuery);
}
})(this, ($) => {
//todo
});
正统 —— ES6/ES2015
时间前进到 2016 年 5 月,经过了两年的讨论,ECMAScript 6.0 终于正式通过决议,成为了国际标准。
在这一标准中,首次引入了 import
和export
两个 JavaScript 关键字,并提供了被称为 ES Module 的模块化方案。