RequireJS知识总结

一、理清概念


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




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值