您的位置:首页 > 产品设计 > UI/UE

RequireJS知识总结

2015-10-22 09:28 417 查看
一、理清概念

1、支持AMD的框架和库有jQuery、underscore、Dojo、MooTools、EmbedJS、qwery、bonzo甚至还有firebug,而backbone这个库,没有采用AMD规范编写。如果要加载它的话,必须先定义它的特征。如下:

      

require.config({
    shim: {

      'backbone': {
        deps: ['underscore', 'jquery'],            //好像不依赖jquery,只依赖underscore
        exports: 'Backbone'
      }
    }
});


     实现AMD规范的框架有require.js、curl.js、bdload、JSLocalnet、Nodules等,它们都实现了define方法,同时都有自己的补充API。

2、近几年大量的前端框架出现,这些框架都在尝试解决一些前端开发中的共性问题,但是实现又不尽相同。在这个背景下,CommonJS社区诞生了,为了让前端框架发展的更加成熟,CommonJS鼓励开发人员一起在社区里为一些完成特定功能的框架制定规范。AMD(Asynchronous Module Definition)就是其中的一个规范。

3、CommonJS的AMD规范中只定义了一个全局的方法:

       define(id?, dependencies?, factory);

id是可选参数,一般不需要,但是有的库显式指定了id,比如:jquery,underscore 。由于他们经常被其他库依赖,如果是无主的话,那么其他的库可能会为他们起不同的模块名,而导致可能会多次载入它们,影响效率。

data-requiremodule="*******"会不断改变。

dependencies也是可选参数,是一个字符串Array,字符串代表模块名id,如果是显式指定模块名的库,那么字符串不能乱写,比如jQuery的模块名固定为"jquery",不能为其他。当没有该参数时,说明不依赖其他模块,实现AMD的框架应提供默认值["require",
"exports", "module"]。

factory是一个回调函数,当前面依赖的所有模块加载完成后,立刻执行。前面依赖模块的返回值以默认顺序作为factory方法的参数。

ps:还有一种数据对象模块:

define({
users:[],
members:[]
});

ps: 里面可以是对象,数字、布尔型。但是不能是字符串或者数组,因为会被当成id或者dependencies参数。

       

4、RequireJS提供了如下功能:

声明不同js文件之间的依赖;
可以按需、并行、延时载入js文件;
可以让我们的代码以模块化的方式组织。

5、require.js要求,每个模块是一个单独的js文件。这样的话,如果加载多个模块,就会发出多次HTTP请求,会影响网页的加载速度。因此,require.js提供了一个优化工具,当模块部署完毕以后,可以用这个工具将多个模块合并在一个文件中,减少HTTP请求数。

6、AMD(Asynchronous Module Definition)规范的典型实现require.js;

      CMD(Common Module Definition)规范的典型实现是sea.js;

      CommonJS规范的典型实现是Node.js,服务器端javascript,引入模块的速度是硬盘的读取速度,而浏览器中的js,是通过http请求从服务器下下来的,所以异步的重要性就体现出来了。

二、RequireJS功能

看require.js的源码,会看到它定义了3个全局变量,requirejs,require,define。其中requirejs和require是一个意思。

require和define的区别是define用来定义一个模块,它的回调函数有返回值,可能是对象或者函数等,任何有效的返回都是可以的(CommonJS/SeaJS则只能是JS对象);而require的回调函数没有返回值。

1、加载js文件

<script src="./js/require.js"></script>
<script>
require(["./js/a.js", "./js/b.js"], function() {
myFunctionA();
myFunctionB();
});
</script>


require方法里的字符串数组可以允许不同的值,当字符串以".js"结尾,或者以"/"开头,或者就是一个URL,那么RequireJS认为用户就是在加载一个js文件。否则,当字符串类似"my/module"格式的时候,它会认为这是一个模块,并且会以用户配置的baseUrl和paths来加载相应的模块。

There may be times when you do want to reference a script directly and not conform to the "baseUrl + paths" rules for finding it. If a module ID has one of the following characterstics, the ID will
not be passed through the "baseUrl + paths" configuration, and just be treated like a regular URL that is relative to the document(document指的是引入requirejs的html页面目录):
*Ends in ".js".
*Starts with a "/".
*Contains an URL protocol, like "http:" or "https:".

2、页面加载后执行js,用RequireJS的domReady插件

<script src="./js/require.js"></script>
<script>
require(["domReady!", "./js/a.js", "./js/b.js"], function() {
myFunctionA();
myFunctionB();
});
</script>

按F12,在页面源码的head标记中会发现插入了如下的代码head.appendChild(),是requirejs做的工作:

<script type="text/javascript" charset="utf-8" async="" data-requirecontext="_" data-requiremodule="js/a.js" src="js/a.js"></script>

其中async属性目前绝大部分浏览器已经支持,表示js文件不会阻塞其他内容的下载。

3、加载js模块

好处:模块一般都有一个返回值,它会作为参数传给回调函数,那么就不需要访问全局变量了,有效的避免了大量而且复杂的命名空间管理。模块名后面不需要.js后缀,requirejs会在运行时自己加上。data-main属性值也不要加上.js。

require(["js/student", "js/class"], function(student, clz) {
clz.addToClass(student.createStudent("Jack", "male"));
clz.addToClass(student.createStudent("Rose", "female"));
console.log(clz.getClassSize());
});


如果某个模块在另一个主机上,则可以指定它的网址,如下:

 require.config({
    paths: {
      "jquery": "https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min"
    }
 });


ps:尽量避免循环依赖,如果实在避免不了,可以在模块中加上对"require"模块的依赖,在回调函数中直接用
       require("dependencyModuleName")。



pps:为了正确的使用这些功能,你定义的所有模块都需要使用RequireJS的API,否则它不会像期望的那样工作。
补充:

1.原生的require()不支持按次序加载,所以加载的JS文件到底先加载哪个,无法事前知道,而之后的回调参数也是在请求的所有JS完毕之后才开始回调;
2.动态加载的Js文件,不在原始的DOM结构之中,因此在DOM-ready(DOMContentLoaded)事件和window.onload事件中指定的回调函数对它无效。

4、require当作依赖模块,使用它中的方法

define(["require"], function(require) {
var cssUrl = require.toUrl("./style.css");             //返回baseUrl/style.css
});


5、require.js插件

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文件。

三、配置RequireJS

<script data-main="js/main" src="scripts/require.js"></script>


data-main 指定了一个在当前index.html目录并行的文件夹下 ./js/main.js作为程序入口,而 js目录也将作为baseUrl在其他模块定义时使用。baseUrl通常设置为data-main指定文件的目录,如上面的js,也即./js。baseUrl也可以通过手动配置,可以是绝对路径也可以是相对路径,相对路径指引入requirejs的页面作为参考点,下面会讲。若没有手动配置,也没有data-main属性的话,那么默认目录为引入requirejs的html所在的目录。

一种更为直接的方式是显式指定baseUrl和paths,利用require.config来配置,示例如下:

<script type="text/javascript" src="./js/require.js"></script>
<script type="text/javascript">
require.config({
baseUrl: "./js",                     ".js"可以写成"./js/"或者"js"或者"js/",但是不能写成"js\\"或"./js\\"!!
paths: {
"some": "some/v1"                 //"some"也可以写成some,键值对的键的引号可加可不加,而值加引号代表字符串
},
waitSeconds: 10
});
require( ["some/module", "my/module", "./js/a.js"],
function(someModule,    myModule) {}
);
</script>

some/module具体的js文件路径是./js/some/v1/module.js,waitseconds是指最多花多长时间来加载一个js文件,默认值为7秒。

关于配置paths的经验:

模块名为name,可以不管它,即baseUrl/name.js;可以配置paths,即baseUrl/somepath/somename.js。
模块名为somepath/name,可以不管它,即baseUrl/somepath/name.js;可以配置somepath,即baseUrl/otherpath/name.js;可以配置"somepath/name",即baseUrl/otherpath/othername.js。

shim参数:

shim参数解决了使用非AMD方式定义的模块及其载入顺序,即不支持AMD规范,如jQuery插件,jQuery插件的本质是将命名空间挂在全局jQuery或jQuery.fn上,而非使用define定义的模块。jQuery插件皆依赖于jQuery,即得保证jQuery先下载。示例如下:

require.config({
shim: {
'jquery-slide': ['jquery']
}
});
require(['jquery-slide']);

ps:好像也可以按照普通的js文件载入,而不用AMD的风格来写,如果有依赖,那么必须用shim参数,有机会可以试下。

上面会暴露jquery-slide中的所有全局变量,如下面《项目实战代码》中的util.js,模块返回值是undefined,要想某个或者某几个全局变量当作这个模块的返回值,可以如下:

requirejs.config({
baseUrl: '/public/js',
paths: {
hello: 'hello'
},
shim: {
hello: { exports: 'hello' }                  //'hello'是hello.js中的hello函数
}
});

requirejs(['hello'], function(hello) {
hello();
});

若想暴露多个变量:用init

requirejs.config({
baseUrl: '/public/js',
paths: {
hello: 'hello'
},
shim: {
hello: {
init: function() {            //当exports和init同时存在时,exports将被忽略。
return {
hello: hello,
hello2: hello2
}
}
}
}
});

requirejs(['hello'], function(hello) {
hello.hello1();
hello.hello2();
});

若暴露变量的同时,还依赖于其他模块,用deps:

require.config({
baseUrl: 'scripts/app',
paths: {
lib: '../lib'
},
shim: {
'backbone': {
//The underscore script dependency should be loaded before loading backbone.js
deps: ['underscore'],
// use the global 'Backbone' as the module name.
exports: 'Backbone'
}
}
});


四、项目实战代码

require.config({
paths : {
views : "../views",
jquery : "lib/jquery/jquery-1.11.1.min",
jqm : "lib/jquery/jquery.mobile-1.4.3.min",
underscore : "lib/underscore/underscore-min",
knockout : "lib/knockout/knockout-3.0.0",
text : "lib/require/text",                                  //require.js的插件
director : "lib/director.min",
jq_i18n : "lib/jquery/jquery.i18n.properties-1.0.9",
jq_translate : "lib/jquery/translate",
jq_validate : "lib/jquery/jquery.validate",
jq_additional : "lib/jquery/additional-methods",
base64 : "lib/base64",
echarts : "lib/echarts",
"echarts/chart/pie" : "lib/echarts"
},
shim : {
jq_translate : ["jq_i18n"],
jq_additional : ["jq_validate"],
util : ["jquery"]
}
});
require(["jquery", "app", "util"], function ($, app) {}


util是非AMD规范的模块,实际上它就是一个工具包,里面全是函数,那么加载它要么当成普通的js文件,写成"util.js"或者"util"(它们的路径相对谁是不一样!),或者用shim参数,因为它还依赖于jquery模块,所以必须用shim了。

五、我的实验

以下是如下目录结构:

|

|......test.html

|......main.js

|......object.js

|......require.js

|......jquery-1.11.1.min.js

1、数据对象模块

test.html

<!DOCTYPE html>
<html>
<head>
<title>test</title>
<meta charset="utf-8">
</head>
<body>
<div>123456789</div>
<script data-main="main" src="require.js"></script>
</body>
</html>

main.js

require(["object"], function(o){
alert(o.name+o.age);
});

object.js

define({
name:'qhairen',
"age":25
});

2、依赖模块有空字符串

require.config({
paths:{
"jquery":"jquery-1.11.1.min"
}
});

require(["object", "", "jquery"], function(o, a, $){
alert(o.name);
alert(a);
alert($);
});

打印出qhairen,undefined,一个函数。说明空字符串也被当做一个模块,没有返回值。

3、不依赖于任何模块,可以不写,或者写一个[],但是define中如果定义了模块id,同时不依赖任何模块,那么[],必须写上

require.config({
paths:{
"jquery":"jquery-1.11.1.min"
}
});

require([], function(o, a, $){
alert(1);
});

require和define的区别在于define可以多个id参数,但是id名字不能乱起,比如上面只能起“main”,且define可以没有返回值,代表返回undefined。

以下是如下目录结构:

|

|......test.html

|......js

         |......main.js

         |......object.js

         |......hello.js

         |......require.js

         |......jquery-1.11.1.min.js

4、模块字符串以.js结尾

test.html

<!DOCTYPE html>
<html>
<head>
<title>test</title>
<meta charset="utf-8">
</head>
<body>
<div>123456789</div>
<script data-main="js/main" src="js/require.js"></script>
</body>
</html>

main.js

requirejs.config({
paths:{
"jquery":"jquery-1.11.1.min"
}
});

require( ["js/hello.js"], function(o, a, $){   //这里是js/hello.js,说明是相对test.html的。也可以写成"hello",则按照baseUrl+paths
hello1();                             //这里说明分两步,第一步head头包含js/hello.js,第二步使用
hello2();                             //hello1,hello2,它们都是全局变量,没有模块概念。

});

hello.js

function hello1(){
alert("hello1");
}

function hello2(){
alert("hello2");
}

把main.js改变下:

requirejs.config({
paths:{
"jquery":"jquery-1.11.1.min"
}
});

require( ["js/object.js"], function(o, a, $){          //或者写成"object",都行,都打印出"qhairen",说明js的查找方式并不会改变
alert(o.name);                                //它是模块的事实。同样按照上面,第一步head头包含js/object.js,第二步
//模块的返回值给o,打印o.name。而第一步由于object.js是模块,仅有一个
//define函数,没有全局变量暴露出来,所以唯一的对外接口就是o了。

});

5、模块的回调函数中有全局变量声明

main.js

requirejs.config({
paths:{
"jquery":"jquery-1.11.1.min"
}
});

require( ["object"], function(o){

alert(a);                      //打印出5,如果写成var a=5;这里会报错,a未定义。
//建议模块中不要定义全局变量。
});

object.js

define(function(){
a = 5;
return {name:'qhairen',
"age":25}
})


以下是如下目录结构:

|

|......test.html

|......jq

         |......jquery-1.11.1.min.js

         |......my.js

|......js

         |......main.js

         |......object.js

         |......hello.js

         |......require.js

6、jquery模块名字永不变,仅改变它的path

main.js

requirejs.config({
paths:{
"jquery":"../jq/jquery-1.11.1.min"         //jquery模块名是固定的!!!
}
});

require( ["jquery", "object"], function($, o){
alert($);
alert(o["name"]);

});

7、若模块有模块名,那么它必须不能变,即在[]中,它必须是固定的

main.js

requirejs.config({
paths:{
"jquery":"../jq/jquery-1.11.1.min",

}
});

require( ["jquery", "object", "../jq\\my"], function($, o, m){    //这里和下面my.js的模块名必须一模一样!!!!

alert(m);
});

my.js

define("../jq\\my", function(){                   //不能写成../jq/my!!!!
return 8;
});
其实不用写成这么复杂,可以类似上面jquery模块的写法,在paths里配置就好

main.js

requirejs.config({
paths:{
"jquery":"../jq/jquery-1.11.1.min",
"my":"..\\jq/my"                                 //这里配置下就好!!

}
});

require( ["jquery", "object", "my"], function($, o, m){

alert(m);
});

my.js

define("my", function(){
return 8;
});


8、关于shim参数的细节

requirejs.config({
paths:{
"jquery":"../jq/jquery-1.11.1.min",
},
shim : {
"hello":{}  //这里也可以写成 "hello":[],或者这一行不写,都会暴露其所有全局变量
}
});

require( ["hello"], function(h){          //h为undefined值
hello1();
hello2();
h();

});


关于exports:

requirejs.config({
paths:{
"jquery":"../jq/jquery-1.11.1.min",
},
shim : {
"hello" : {
exports : "hello1",        //这里hello1必须加引号!!!
}
}
});

require( ["hello"], function(h){              //h值为hello1,
hello1();
hello2();                            //hello1(),hello2()照样有效,说明全局变量该暴露还是暴露的!!
h();

});


关于init:

requirejs.config({
paths:{
"jquery":"../jq/jquery-1.11.1.min",
},
shim : {
"hello" : {
exports : 'hello1',
init : function(){
return {
hello11 : hello1,           //这里hello1,hello2必须不能加引号!!!与上区别!
hello22 : hello2            //exports和init同时存在时,只认init。
};
}
}
}
});

require( ["hello"], function(h){
hello1();
hello2();                  //hello1(),hello2()还是有用的,说明全局变量还是暴露出来的!
h.hello11();
h.hello22();

});


六、参考链接

1、中文API:  http://www.requirejs.cn/home.html

2、英文API:  http://www.requirejs.org/

3、RequireJS Plugins:  https://github.com/jrburke/requirejs/wiki/Plugins

4、大神们的博客:

                     http://www.cnblogs.com/yexiaochai/p/3214926.html

                     http://www.cnblogs.com/snandy/archive/2012/05/22/2513652.html

                     http://www.cnblogs.com/snandy/archive/2012/05/23/2513712.html

                     http://www.cnblogs.com/snandy/archive/2012/05/24/2514700.html

                     http://www.tuicool.com/articles/jam2Anv

                     http://www.ibm.com/developerworks/cn/web/1209_shiwei_requirejs/

                     http://www.oschina.net/translate/getting-started-with-the-requirejs-library?p=1#comments

                     http://www.ruanyifeng.com/blog/2012/10/javascript_module.html

                     http://www.ruanyifeng.com/blog/2012/10/asynchronous_module_definition.html

                     http://www.ruanyifeng.com/blog/2012/11/require_js.html

                     http://www.cnblogs.com/snandy/archive/2012/03/12/2390782.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: