这篇文章观点主要来源阮一峰的博客及自己的理解,这里写出来一是为了加强自己的记忆,二是希望和大家一起分享,如果有错误希望大家指出。
模块通俗来说就是包含有很多方法的集合,感觉有点像别的语言中的库文件,在网页应用中JavaScript的使用越来越广泛,一个网页中所用到的js文件也越来越多,管理起这些文件就显得非常混乱,首先看看js的模块化写法,
js的模块化
如果将所有方法都直接写在全局域里面,那么就会污染全局变量,甚至占用很多有用的名字,取名也会费一番脑经,这种方式很不适合大型开发
function f1(){
//...
}
function f2(){
//...
}
//...
改进的写法就是将这些方法写入一个对象中,但是如果模块里面有一些不希望暴露给使用者的属性或方法,就没有办法了
var module=new Obeject(
f:1,//不希望给外面调用
f1:function(){
//...
},
f2:function(){
//...
},
//...
);
这就出现了立即执行函数这种写法,这个函数返回一个希望暴露给外部的一系列方法或属性的对象集合(甚至还可以用新的名字去替换原先模块里的名字),并直接定义一个变量去引用这个对象
var module1=(function(){
f:1,//不希望给外面调用
var f1=function(){
//...
}
var f1=function(){
//...
},
//...
return {
out_f1:f1,
out_f2:f2
};
})();
这里将f这个属性向外界隐藏了,这种方式还可以对function传入其他全局变量甚至传入它自己,用以分次添加模块的方法,可以很方便的临时添加功能
//传入其他全局变量,比如类库
var module2=(fuction($,mod,mod1){
//使用jquery操作等
mod.f3=mod1.dosomething;//将别的模块的方法添加到这里来
return mod;//返回修改后的module2
})(Jquery,module2,module3)
现行的模块规范有两种,一种是CommonJs,另一个是AMD。
由于nodejs的出现,让JavaScript不再局限于网页端编程,进入了服务器的领域,对于复杂的后端逻辑避免不了大量的模块引用,在nodejs中,模块是采用CommonJs的规范写的,使用下面的方式去加载和使用一个写好的math模块
var math = require('math');
math.add(2,3); // 5
这种方式在浏览器环境中是不适合的,要使用这个模块的方法必须等这个加载过程结束才可以,模块存储在服务器端,从浏览器端加载模块会因为各种各样的原因导致加载缓慢,这样页面就会停滞,这会大大影响用户的体验。
因此在浏览器端不适合同步加载,必须使用异步加载,于是AMD规范出现了,AMD是"Asynchronous Module Definition"的缩写,意思就是“异步模块定义”。 它采用的也是require方法去加载模块,但是它的参数带有一个回调函数,也就是加载成功就会执行那个函数
require([module], callback);//两个参数,一个模块名,一个回调函数
require(['math'], function (math) {
math.add(2, 3);
});
现在主要有两个Javascript库实现了AMD规范:require.js和curl.js。
require.js的使用
最早的时候,js文件只需要几个就够了,js文件之间的依赖关系也没有那么密切,然而现在的情况不同了,经常会见到下面的代码
<script src="1.js"></script>
<script src="2.js"></script>
<script src="3.js"></script>
<script src="4.js"></script>
<script src="5.js"></script>
<script src="6.js"></script>
相互之间还会有依赖关系,比如很多js文件都很依赖jquery这个库,所以如果加载顺序不同,会很麻烦,因此要严格遵照依赖性顺序,依赖性最大的放到最后,require.js的出现主要是为了两个问题,一是实现异步加载js文件,防止网页失去响应,二是为了管理模块之间的依赖关系,方便编写和维护。
和别的js模块一样,要使用它首先要去官网下载一个最新的版本,加载这个文件时可能也会让网页失去反应,可以在网页的最后面去加载,也可以使用下面的方式加载
<script src="js/require.js" defer async="true" ></script>
其中defer是为了支持IE,它不支持async属性,这样就可以异步加载这个js文件了,这样只是加载了require.js,还要给data-main属性写一个入口文件,制定网页程序的主模块
<script src="js/require.js" data-main="js/main"></script>
其中main.js就是我们的主模块,假设也是在js文件下,
主模块的写法
如果不依赖任何模块,就可以直接写JavaScript代码,但是这就没必要用require.js了,
要加载模块,就要使用require函数
// main.js
require(['moduleA', 'moduleB', 'moduleC'], function (moduleA, moduleB, moduleC){
// some code here
});
一般的加载方式就是这样,首先接受一个模块数组参数,然后有一个回调函数,数组作为函数的参数传入,模块数组中的模块都是异步加载的
模块的加载
在下面的例子中
require(['jquery', 'underscore', 'backbone'], function ($, _, Backbone){
// some code here
});
加载了三个模块,但是并没有提这些模块的具体地址、也没有对回调函数的传参使用别名,这里还需要在require.config方法中进行设置
require.config({
baseUrl:"js/lib",//相对main.js的目录位置
path:{
"jquery": "jquery.min",//在这个目录下的文件名,可忽略js后缀
"underscore": "underscore.min",
"backbone": "backbone.min"
}
});
还可以直接写cdn地址
require.config({
paths: {
"jquery": "https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min"
}
});
AMD模块的写法
require.js要求加载的模块按照AMD的规范去写
假设有一个math模块,就要这样去写
\\mah.js
define( function()
{
var add=function(x,y)
{
return x+y;
};
return{add:add}
});
加载
//main.js
require(["math"],function(math){
alert(math.add(1,2));
});
如果这个模块还依赖其他模块,那么define()函数的第一个参数,必须是一个数组,指明该模块的依赖性。
define(['myLib'], function(myLib){
function foo(){
myLib.doSomething();
}
return {
foo : foo
};
});
加载非AMD规范模块
实际上有很多模块并不是按照AMD规范去写的,require.js加载这些模块需要在require.config中的shim属性中添加相应的依赖关系和导出标记
require.config({
shim:{
'underscore':{
exports:"_"
},
'backbone':{
deps:['underscore', 'jquery'],
exports: 'Backbone'
}
}
});
require.js还提供一系列[插件],实现一些特定的功能。
domready插件,可以让回调函数在页面DOM结构加载完成后再运行。
require(['domready!'], function (doc){
// called once the DOM is ready
});
text和image插件,则是允许require.js加载文本和图片文件。
define([
'text!review.txt',
'image!cat.jpg'
], function(review,cat){
console.log(review);
document.body.appendChild(cat);
}
);
类似的插件还有json和mdown,用于加载json文件和markdown文件。