javascript中模块的发展历程
一、模块的重要性
Javascript不是一种模块化编程语言,它不支持”类”(class),更别说”模块”(module)了。
开发者们做了很多努力,在现有的运行环境中,实现”模块”的效果。
没有模块的日子里
1 | function a(){ |
后果:
全局变量的灾难;
函数命名冲突;
依赖关系不好管理。
js模块的前身(为了解决以上问题)
1. 面对对象的写法:1
2
3
4
5
6var obj = {
a:1,
b:function(){
...
}
};
优点:
1、解决了变量污染的问题;
2、保证模块名唯一即可,建立同一模块内的成员的关系。
缺点:
1、暴露所有模块成员,内部状态可以被外部任意改写。1
obj.a =100;
2. 匿名自执行函数:1
2
3
4
5
6
7
8(function(){
var obj = {
a:1,
b:function(){
...
}
};
})();
优点:
1、解决暴露所有模块成员,内部状态可以被外部任意改写的问题。
缺点:
1、所需依赖还是得外部提前提供。
二、commonJS
2009年,对js是历史性的一年,nodeJS横空出世,让js跑在服务端,如果说js在浏览器上面可以没有模块,但是在服务端没有模块的思想是万万不能容忍的。
由Mozilla 的工程师 Kevin Dangoor 在2009年1月创建了commonJS规范。1
2
3
4
5
6
7
8
9
10
11
12//创建模块 one.js
var a = 'aaaa';
function b(){
console.log(a);
};
module.exports = {
a: a,
b: b
};
//加载模块 two.js
var x = require('./one.js');
x.b();
优点:
1、所有代码都运行在模块作用域,不会污染全局作用域;
2、独立性是模块的重要特点就,模块内部最好不与程序的其他部分直接交互;
3、模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。要想让模块再次运行,必须清除缓存;
4、模块加载的顺序,按照其在代码中出现的顺序。
node推广了commonJS规范,但是在浏览器中又出现了很多问题
问题:
浏览器资源的加载方式与服务端完全不同。服务端require一个模块,直接就从硬盘或者内存中读取了,消耗的时间可以忽略。而浏览器则不同,需要从服务端来下载这个文件,然后运行里面的代码才能得到API,需要花费一个http请求,也就是说,require后面的一行代码,需要资源请求完成才能执行。
由于浏览器端是以插入script标签的形式来加载资源的(ajax方式不行,有跨域问题)没办法让代码同步执行,所以像commonjs那样的写法会直接报错。
这意味着要想适应浏览器,规范还要改进!!!
一个Modules/Wrappings规范出现了。
三、AMD
经过一番谈论、修改(过程忽略),AMD思想出现了…
AMD(Asynchronous Module Definition):
- 异步模块定义规范制定了定义模块的规则,这样模块和模块的依赖可以被异步加载。这和浏览器的异步加载模块的环境刚好适应(浏览器同步加载模块会导致性能、可用性、调试和跨域访问等问题)。
- 依赖前置,预执行(异步加载:依赖先执行,依赖必须一开始就写好,会先尽早地执行(依赖)模块 。换句话说,所有的require都被提前执行(require 可以是全局或局部 )。
- 相关Api(简单实例):
1
2
3
4
5
6
7
8//定义和暴露模块
define("xxx", ["xxx", "xxx"], function(x, x) {
return ... ;
});
//加载模块
require(["xxx", "../xxx"], function(xxx, xxx) {
xxxx
});
具体Api请参见:AMD (中文版)
RequireJS
说完AMD,就不得不提把AMD在浏览器实现的RequireJS。
RequireJS是一个JavaScript文件和模块加载器,采用AMD规范。
参考文件:RequireJS
三、CMD
CMD(Common Module Definition):
- CMD更贴近 CommonJS Modules/1.1 和 Node Modules 规范,一个模块就是一个文件;
- 它推崇依赖就近,想什么时候 require 就什么时候加载,实现了懒加载(延迟执行 );
- 它也没有全局 require, 每个API都简单纯粹;
- 不过RequireJS从2.0开始,也改成可以延迟执行。
- 相关Api(简单实例):
1
2
3
4
5
6
7
8
9
10
11//定义和暴露模块
define('xxx', ['xxx'], function(xxx, xxx, xxx) {
return ... ;
});
//加载模块
define(function(require, exports) {
// 获取模块 a 的接口
var a = require('./a');
// 调用模块 a 的方法
a.doSomething();
});
seaJS
- SeaJS遵循的CMD,将CMD在浏览器中实现;
- SeaJS 是一个模块加载器;
- 借鉴了 RequireJS 的不少东西
其他
UMD
既然CommonJs和AMD风格一样流行,似乎缺少一个统一的规范。所以人们产生了这样的需求,希望有支持两种风格的“通用”模式,于是通用模块规范(UMD)诞生了。
es6 modules
- ES6自带了模块化, 也是JS第一次支持module;
后记
本文主要记述模块的思想在浏览器的实现过程,不是一篇详细的Api教程。