require.js是一个遵循AMD规范,可以实现.js文件按需分块加载的前端框架。举个例子,我们在写前端代码时,常常会看到如下的写法:
<script src="1.js"></script>
<script src="2.js"></script>
<script src="3.js"></script>
<script src="4.js"></script>
这段代码依次加载多个js文件,这样的写法有很大的缺点。首先,加载的时候,浏览器会停止网页渲染,加载文件越多,网页失去响应的时间就会越长;其次,由于js文件之间存在依赖关系,因此必须严格保证加载顺序(比如上例的1.js要在2.js的前面),依赖性最大的模块一定要放到最后加载,当依赖关系很复杂的时候,代码的编写和维护都会变得困难。使用
require.js框架即可彻底解决这种难题。
我们可以去官网下载require.js,使用该框架,在我们的html页面中常常如下引入:
<script src="js/libs/require.js" defer async="true" data-main="js/main"></script>
async属性表明这个文件需要异步加载,避免网页失去响应。IE不支持这个属性,只支持defer,所以把defer也写上。data-main则表示我们自己的代码主文件所在路径。在上例中,就是js目录下面的main.js,这个文件会第一个被require.js加载。由于require.js默认的文件后缀名是js,所以可以把main.js简写成main。
main.js作为require.js第一个引入的js文件,自然要遵循一定的写法规范。首先,require.js主要目的是支持js按需分块加载,所以第一步是把相关的js文件按模块引入,写法如下:
//模块的配置
requirejs.config({
baseUrl: "./",
paths: {
"jquery": "js/libs/jquery-1.8.0",
"common": "js/libs/common",
"countObject": "js/core/countObject",
"dependCount": "js/core/dependCount",
"countFunction": "js/core/countFunction"
}
});
其中,baseUrl表示main.js目录的相对目录,依据相对目录规则可知"./"表示当前引入文件基目录与main.js在同一目录层级下;paths表示各个引入js文件在以baseUrl为基目录的基础上的详细目录地址。已jquery为例:jquery的引用目录是"./js/libs/jquery-1.8.0"。
当前Demo的目录结构如图所示:
(注)此步requirejs.config引用模块中,虽指出了各个js所在路径,但实际上并没有加载进来,实际加载进来是在接下来的模块引用代码中生效。
在上一步中js文件的引用配置已经配好了,接下来就是对所需的模块进行加载。模块加载的代码如下:
//应用程序
requirejs(['_main_']);
define("_main_", ["jquery", "common", "countObject", "countFunction", "dependCount"], function($, common, countObject, countFunction, dependCount) {
//your code
});
模块使用define()函数来定义,上面代码中define("_main_",.......)表示将main.js定义为名称是"_main_"的模块,requirejs(['_main_']);则表示引用该模块。
这种定义模块名称的写法常常用在1对1的关系中,即1个html文件相对应1个主js文件,如demo中的index.html中的data-main="js/main"相对应的1个js文件是main.js。其他自定义模块引用如后面将介绍到的countObject.js,定义时无需定义模块名称,用define(function() {});即可。
define(...,[?],....)中[?]是一个字符串数组,字符串值为requirejs.config配置中paths的属性值,根据实际需要引入main.js中所需的模块,define(...,...,function(?,?,?){});中的function(?,?,?){}表示这些模块的外部调用名称,正常情况下与引入模块名称一致。
解决了模块的引用和加载,接下来就是这些模块的在代码中如何使用了。如对jquery模块的引用,由于该模块定义的外部名称是$,所以在使用时按照jquery的习惯使用$即可调用,其他模块调用大致相同,如countObject.add(1,1),表示送出1加1的结果。接下来主要讲讲countObject、countFunction和dependCount这三种常用的自定义模块的写法。
第一种是没有依赖于任何其他模块的自定义模块,如本Demo中的countObject:
//非依赖对象定义
define(function() {
var module = {};
module.value = 0;
//加法
var add = function(a, b) {
return a + b;
}
//减法
var minus = function(a, b) {
return a - b;
}
module.add = add;
module.minus = minus;
return module;
});
第二种是有依赖于其他模块的自定义模块,如dependCount:
//依赖对象定义
define(['./countObject'], function(countObject) {
var module = {};
//加法
var add = countObject.add;
//减法
var minus = countObject.minus;
module.add = add;
module.minus = minus;
return module;
});
这两种模块的使用方法都是通过“.“的来引用,如countObject.add(1,1)。
还有一种写法是用过new Object()的方法来引用,这类模块的写法如下:
define(function() {
var target = null;
function countTool() {
target = this;
target.value = 0;
};
countTool.prototype.add = function(a, b) {
return a + b;
};
countTool.prototype.minus = function(a, b) {
return a - b;
};
return countTool;
});
使用方法是:
var c = new countFunction();
var r = c.add(1,1);
console.log(r); // 2
理论上,require.js加载的模块,必须是按照AMD规范、用define()函数定义的模块。但是实际上,虽然已经有一部分流行的函数库(比如jQuery)符合AMD规范,更多的库并不符合。那么,require.js是否能够加载非规范的模块呢?回答是可以的,在配置项时使用shim即可。不符合AMD规范的第三方库常见的有两种写法:第一种写法是
//dateUtil.js
(function(window) {
var DateUtils = {};
DateUtils.toString = function() {
alert("toString");
};
// 全局变量
window.DateUtils = DateUtils;
})(window);
另一种写法是:
//stringUtil.js
var StringUtils = {};
StringUtils.toUpperCase = function(input) {
alert("toUpperCase");
}
要引用这两种脚本,可以使用如下代码:
//模块的配置
requirejs.config({
baseUrl: "./",
paths: {
"jquery": "js/libs/jquery-1.8.0",
"common": "js/libs/common",
"countObject": "js/core/countObject",
"dependCount": "js/core/dependCount",
"countFunction": "js/core/countFunction",
"dateUtil": "js/core/dateUtil",
"stringUtil": "js/core/stringUtil"
},
shim: {
dateUtil: {
deps: [],
exports: 'DateUtils'
},
stringUtil: {
deps: [],
exports: 'StringUtils'
}
}
});
//应用程序
requirejs(['_main_']);
define("_main_", ["jquery", "common", "countObject", "countFunction", "dependCount", "dateUtil", "stringUtil"], function($, common, countObject, countFunction, dependCount, dateUtil, stringUtil) {
//your code
stringUtil.toUpperCase();
dateUtil.toString();
window.StringUtils.toUpperCase();
window.DateUtils.toString();
});
shim中deps表示该js的依赖项,如依赖于jquery,则为deps: [jquery],没有依赖则为空数组。第二个参数exports表示该js对外应用时的名称,即js中的对外应用的全局变量,如dateUtil.js中的DateUtils。在实际应用中,还有一类第三方库的情况,它自身也定义了一个require,如下图所示psd.js库
这样的库必然会与require.js中的require相冲突,这时想要在require.js框架中使用这类自带require函数的第三方库,怎么办呢?首先,可以在使用psd.js库前,把已有的require.js框架全局变量保存下来,然后将psd.js动态加载进来,使用完成后,在对require.js框架全局变量进行重置。
//使用前
var _define = null;
var _requirejs = null;
var _require = null;
_define = window.define;
_requirejs = window.requirejs;
_require = window.require;
window.requirejs = null;
window.require = null;
window.define = null;
//加载psd.js
loadFiles([url + '/psd.js'
], function() {
//使用psd.js
//some code
//使用后
window.define = _define;
window.requirejs = _requirejs;
window.require = _require;
});
// 动态加载js文件
function loadFiles(urlArrValue, callback) {
var staticPath = "";
var urlArr = urlArrValue;
var filesNum = urlArr.length;
var loadedCount = 0;
if(filesNum > 0) {
var url = staticPath + urlArr[loadedCount];
Load(url);
}
function Load(url) {
var f;
if(url.indexOf(".css") > 0) {
f = document.createElement("link");
f.type = 'text/css';
f.rel = "stylesheet";
f.href = url;
} else {
f = document.createElement("script");
f.type = "text/javascript";
f.src = url;
}
f.onload = function(e) {
loadedCount++;
if(loadedCount < filesNum) {
var url = staticPath + urlArr[loadedCount];
Load(url);
} else {
callback();
}
}
f.onerror = function(e) {
loadedCount++;
if(loadedCount < filesNum) {
var url = staticPath + urlArr[loadedCount];
Load(url);
} else {
callback();
}
}
document.head.appendChild(f);
}
}
实例Demo下载地址:http://download.csdn.net/detail/zeping891103/9885475