js模块化概念

何为模块?模块就是实现特定功能的一组方法。

模块化的好处就不多说了,我们就介绍一些基本点,可以在以后的项目开发中应用到。

一般的写法

简答的把函数和变量放在一起。

1
2
3
4
5
6
7
function m1(){
...
}
function m2(){
...
}

这种做法简单方便,但是很容易污染了全局变量,很容易与其他模块发生命名冲突,而且模块之间看不出来直接的关系。

对象的写法

只要在上面的基础上稍作修改就能规避很多问题,我们把模块写成一个对象,该模块的方法和成员都放到这个对象里面。

1
2
3
4
5
6
7
8
9
var module1 = new Objec({
name : 'xhay',
m1 : function(){
...
},
m2 : function(){
...
}
});

通过这种方式很好的把成员和方法封装在一起了,这种方式很常用。但是它会暴露所有的成员变量,像这样module1.name = ‘xxx’的用法并不推荐使用。

立即执行函数的写法

我们都知道在javascript中变量的作用域是函数,所以我们可以利用自执行函数来解决上面暴露成员变量的缺点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var module1 = (function(){
var name = 'xhay';
var m1 = function(){
...
}
var m2 = function(){
...
}
return {
m1:m1,
m2:m2
}
})();

这种方式的关键在于return,如果没有return的内容也就没有任何意义了。

如果我们要扩展这个模块我们可以这么做:

1
2
3
4
5
6
7
8
var module1 = (function(mod){
mod.m3 = function(){
...
}
return mod
})(module1);

在稍微演进一下,防止module1是空对象:

1
2
3
4
5
6
7
8
var module1 = (function(mod){
mod.m3 = function(){
...
}
return mod
})(window.module1||{});

如果说为了使我们的模块独立性更强,需要用到的相关js库都要传入模块内

1
2
3
4
5
var module1 = (function($){
...
})(jQuery);

javascript模块规范

就像javascript一样,当我们发现他的好处后,各种规范也相继产生了。在模块化的重要性提高的同时,也产生了相应的规范。目前,比较流行的模块规范有两种:CMD和AMD。

CMD

我们先来说CommonJS,在CommonJS中,有一个全局性方法require(),用于加载模块。我们熟悉的NodeJS就是建立在CommonJS的规范上的。

假定有一个数学模块math.js,就可以像下面这样加载。

1
2
3
var math = require('math');
math.add(1,23);

像这样很方便的math的模块引进来。

在CMD的规范当中推崇的是依赖就近原则,我们需要用到谋个模块时就近加载。

这样的模式如果是应用在服务端还是相当不错的。

但是对于浏览器来说,如果需要的时候再去加载这个模块就会导致浏览器停止在这里等待加载。对于访问者来说这是不能容忍的。

@Molen 指出CMD规范中,使用require执行某个模块并不是去后台请求这个模块。的确是这样的,其实这个模块的资源早就已经加载完成了,只是说通过require是在使用这个模块的时候才去解析或执行。具体区别请看后面补充的例子

所以针对我们特殊的环境,应用同步加载的方式是不可行的。

AMD

RequireJS就是AMD(异步模块定义)的一种实现,和我们的ajax有点儿类似,它采用依赖前置的特点,充分的利用了回调函数的优势,解决浏览器环境的问题。

这种方式也是通过require加载模块的,但不同的是他有两个参数;

1
require([module],callback);

第一个参数是数组,表示需要加载的模块;第二个参数就是我们的回调函数了,等前面的模块加载完成后执行。我们改写上面的例子:

1
2
3
require(['math'],function(math){
math.add(1.23);
});

通过这样的方式就解决了上面出现的浏览器在加载模块时出现的假死的问题。

总结

本文主要讲解了js模块化的一些基本内容以及提出了AMD和CMD这两种规范的差别和基本应用。

大部分内容摘录自:阮一峰的网络日志

补充

解释前面删除线提到的错误:

我们假设用户输入两个数字后点击确定按钮,用我们自己写的运算模块calculation.js进行加法运算,我们假设我们的运算模块文件很大,运算量复杂。分别用AMD规范实现的requirejs和CMD规范实现的seajs来看一下效果。

具体实现代码可以参考:https://github.com/xhay1122/modulejs_demo/tree/master

在AMD规范实现的requirejs当中,在点击确认按钮之前,浏览器并不会加载calculation.js,而是在我们点击确定后通过require使用计算模块才去加载的calculation.js,等计算模块加载解析完成后再执行回调函数。

在CMD规范实现的seajs当中,页面在最开始就已经根据require加载好了需要用到的所有模块(这里只有运算模块calculation.js),而我们在点击确定后通过require使用模块的时候他其实是完成的一个解析或执行的过程。

参考资料:

JavaSript模块规范-AMD规范与CMD规范介绍: http://blog.chinaunix.net/uid-26672038-id-4112229.html

AMD规范文档: https://github.com/amdjs/amdjs-api/wiki/AMD

CMD模块定义规范: https://github.com/seajs/seajs/issues/242

模块系统: https://github.com/seajs/seajs/issues/240

前端模块化开发的价值: https://github.com/seajs/seajs/issues/547

前端模块化开发那点历史: https://github.com/seajs/seajs/issues/588