require
RequireJs
RequireJS是一个js脚本加载器,它遵循AMD(Asynchronous Module Definition,即异步模块加载机制 )规范。实现js脚本的异步加载,不阻塞页面的渲染和其后脚本的执行,并提供了在加载完成之后执行相应回掉函数的功能。
为什么使用RequireJs
实例1:
// index.html
<!DOCTYPE html><html>
<head>
<script type="text/javascript" src="a.js"></script>
</head>
<body>
<span>body</span>
</body></html>
// a.js
function fun1(){ alert("it works");} fun1();
当运行上面两种例子时,alert执行的时候,html内容是一片空白的,即body并未被显示,当点击确定后,才出现,这就是js阻塞浏览器渲染导致的结果
使用require改写上面例子:如下:
// index.html
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="require.js"></script>
<script type="text/javascript">
require(["a"]);
</script>
</head>
<body> <span>body</span> </body>
</html>
// a.js
define(function(){
function fun1(){
alert("it works");
}
fun1();
})
浏览器提示了”it works”,说明运行正确,但是有一点不一样,这次浏览器并不是一片空白,body已经出现在页面中
实例2:
在我们引入js文件时,由于js文件之间存在依赖关系,因为必须严格保证加载的顺序,依赖性最大的模块一定要放在js引入的最后面,这样当依赖关系比较复杂的时候,代码的编写和维护相对就会困难,例如:
// index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
</body>
<script src="js/jquery.min.js"></script>
<script type="text/javascript" src="js/index.js"></script>
</html>
// index.js
(function(){
$("body").css("background-color","lightblue");
})();
在上述demo中,index.js的引入必须要在jquery引入之后。这样在使用框架和js文件较多的时候,他们的导入顺序就会变的复杂。
由上可知:requirejs可解决如下问题:
- 防止js加载阻塞页面渲染。
- 管理模块之间的依赖,便于代码的编写和维护。
- 使用程序调用的方式加载js,防出现如下丑陋的场景
<script type="text/javascript" src="a.js"></script>
<script type="text/javascript" src="b.js"></script>
<script type="text/javascript" src="c.js"></script>
<script type="text/javascript" src="d.js"></script>
<script type="text/javascript" src="e.js"></script>
<script type="text/javascript" src="f.js"></script>
<script type="text/javascript" src="g.js"></script>
<script type="text/javascript" src="h.js"></script>
<script type="text/javascript" src="i.js"></script>
<script type="text/javascript" src="j.js"></script>
基本API
- define:用于定义一个模块
- require:加载依赖的模块,并执行加载完成后的回掉函数
简单键值对
define({
name: "john",
age : 20,
sex : "femal"
});
这种情况用途不是很大,一般用于数据模拟 ,在引入后,回掉函数注入的参数就是该键值对的object对象,如下:
require(['js/index'],function(data){
console.log(data);
});
无依赖的函数式模块定义
define(function(){
function fn(){
alert('it works');
}
fn();
});
有依赖的函数是模块定义:当模块a依赖与模块b的时候,那么在定义模块a时,需要对模块b进行加载,如下:
// index.html
<script src="js/require.js"></script>
<script>
require(['js/index'],function(){
console.log('模块加载成功');
});
</script>
// index.js
define(['./jquery.min'],function(){
$('body').css('background','lightgreen');
});
模块的加载(require中的依赖是一个数组,即使只有一个模块,也需要写成数组的形式;第二个参数为callback,用于处理加载完毕后的逻辑,如下:)
require(["js/index"],function(){
alert("load finished");
})
文件的加载
1) 在上面的demo中,如果index.js依赖模块jquery,那么是通过如下方式引入的,这个./的路径是相对index.js的相对路径require(['./jquery.min'],function(data){});
- 除上面的方式以外,我们还可以通过require.config()方法,对模块的加载行为进行定义
- require.config是用来配置模块加载位置,简单点说就是给模块起一个更短更好记的名字,比如将百度的jquery库地址标记为jquery,这样在require时只需要写[“jquery”]就可以加载该js,本地的js我们也可以相应配置:
require.config({
paths : {
"jquery" : "js/jquery.min",
"index" : "js/index"
}})
require(["jquery","index"],function($){
$(function(){
alert("load finished");
})})
2)除令模块有一个更好记的模块名称,paths还有一个重要的功能,就是可以配置多个路径,如果远程的cdn库没有加载成功,可以加载本地文件,如下当引入jquery时,如果百度的jquery没有加载成功,那么会加载本地的:
require.config({
paths : {
"jquery" : ["http://libs.baidu.com/jquery/2.0.3/jquery", "js/jquery"]
}
})
3)在上面的配置中,我们发现每个文件都是在js文件夹下,此时我们可以使用设置baseUrl,这样在下面的路径中就不选哟每次都写’js/’,如下所示:
require.config({
baseUrl:'js',
paths: {
"jquery": "jquery.min",
"bootstrap": "bootstrap"
}
})
4)上面例子中的callback函数中发现有$参数,这个就是依赖的jquery模块的输出变量,如果你依赖多个模块,可以依次写入多个参数来使用:
require(["jquery","underscore"],function($, _){
$(function(){ _.each([1,2,3],alert); }
)})
如果某个模块不输出变量值,则没有,所以尽量将输出的模块写在前面,防止位置错乱引发误解
全局配置
- 上面的例子中重复出现了require.config配置,如果每个页面中都加入配置,必然显得十分不雅,requirejs提供了一种叫”主数据”的功能, 我们可以将所有的require.config配置放到main.js中,在页面中调用它
require.config({
paths: {
"jquery": "js/jquery.min",
"bootstrap": js/"bootstrap"
}
})
- 然后再页面中使用下面的方式来使用requirejs:
<script data-main="js/main" src="js/require.js"></script>
- 注意:由于在main.js中所设置的脚本是异步加载的,所以如果在页面中配置了其它的js加载,则不能保证他们所依赖的ja已经加载成功,如下,在加载的时候,可能会导致index.js中需要的依赖还未加载完成:
<script data-main="./main.js" src="js/require.js"></script>
<script src="js/index.js"></script>
因此:需要使用require(['js/index'])
对js进行引入,或者对main.js使用script标签进行引入
第三方模块
- 通过require加载的模块一般都需要符合AMD规范即使用define来申明模块,但是部分时候需要加载非AMD规范的js,这时候就需要用到另一个功能:shim:
1)非AMD模块输出,将非标准的AMD模块变成可用的模块,例如:在老版本的jquery中,是没有继承AMD规范的,所以不能直接require[“jquery”],这时候就需要shim
require.config({
shim: {
"jquery" : {exports : "$"}
}
})
2)插件形式的非AMD模块,我们经常会用到jquery插件,而且这些插件基本都不符合AMD规范,比如jquery.form插件,这时候就需要将form插件加入到jquery中:
require.config({
shim: {
"jquery.form" : {
deps : ["jquery"] // jquery.form所依赖的模块
}
}
})
require.config(["jquery", "jquery.form"], function($){
$(function(){
$("#form").ajaxSubmit({...});
})
})
3)在shim中还有一个属性为exports,exports为导出的对象,用法如下:
// operation.js
var myOperation = {}; // 很重要,和shim中exports值必须一致
myOperation.add = function(num1, num2) {
return num1 + num2;
};
myOperation.max = function() {
return Math.max.apply(Math, [].slice.call(arguments));
}
// main.js
require.config({
baseUrl:'js',
paths: {
"myOperation":"operation"
},
shim: {
"myOperation" : {
exports:"myOperation"
}
}
})
// index.js
require(['myOperation'],function(myOperation){
console.log(myOeration);
});
注意:require.config配置shim中exports的值,一定要与相关文件中暴露出全局变量名称一致