正文 19236字数 100,466阅读

模块的写法

一、原始写法
模块就是实现特定功能的一组方法。
只要把不同的函数(以及记录状态的变量)简单地放在一起,就算是一个模块。
  function m1(){     //...   }   function m2(){     //...   }
Run code
Cut to clipboard

    上面的函数m1()和m2(),组成一个模块。使用的时候,直接调用就行了。
    这种做法的缺点很明显:"污染"了全局变量,无法保证不与其他模块发生变量名冲突,而且模块成员之间看不出直接关系。

    二、对象写法
    为了解决上面的缺点,可以把模块写成一个对象,所有的模块成员都放到这个对象里面。
      var module1 = new Object({     _count : 0,     m1 : function (){       //...     },     m2 : function (){       //...     }   });
    Run code
    Cut to clipboard

      上面的函数m1()和m2(),都封装在module1对象里。使用的时候,就是调用这个对象的属性。
      module1.m1();
      Run code
      Cut to clipboard

        但是,这样的写法会暴露所有模块成员,内部状态可以被外部改写。比如,外部代码可以直接改变内部计数器的值。
        module1._count = 5;
        Run code
        Cut to clipboard


          三、立即执行函数写法
          使用"立即执行函数"(Immediately-Invoked Function Expression,IIFE),可以达到不暴露私有成员的目的。
            var module1 = (function(){     var _count = 0;     var m1 = function(){       //...     };     var m2 = function(){       //...     };     return {       m1 : m1,       m2 : m2     };   })();
          Run code
          Cut to clipboard

            使用上面的写法,外部代码无法读取内部的_count变量。
            console.info(module1._count); //undefined
            Run code
            Cut to clipboard

              module1就是Javascript模块的基本写法。下面,再对这种写法进行加工。

              四、放大模式
              如果一个模块很大,必须分成几个部分,或者一个模块需要继承另一个模块,这时就有必要采用"放大模式"(augmentation)。
                var module1 = (function (mod){     mod.m3 = function () {       //...     };     return mod;   })(module1);
              Run code
              Cut to clipboard

                上面的代码为module1模块添加了一个新方法m3(),然后返回新的module1模块。

                五、宽放大模式(Loose augmentation)
                在浏览器环境中,模块的各个部分通常都是从网上获取的,有时无法知道哪个部分会先加载。如果采用上一节的写法,第一个执行的部分有可能加载一个不存在空对象,这时就要采用"宽放大模式"。
                  var module1 = ( function (mod){     //...     return mod;   })(window.module1 || {});
                Run code
                Cut to clipboard

                  与"放大模式"相比,"宽放大模式"就是"立即执行函数"的参数可以是空对象。

                  六、输入全局变量
                  独立性是模块的重要特点,模块内部最好不与程序的其他部分直接交互。
                  为了在模块内部调用全局变量,必须显式地将其他变量输入模块。
                    var module1 = (function ($, YAHOO) {     //...   })(jQuery, YAHOO);
                  Run code
                  Cut to clipboard

                    上面的module1模块需要使用jQuery库和YUI库,就把这两个库(其实是两个模块)当作参数输入module1。这样做除了保证模块的独立性,还使得模块之间的依赖关系变得明显。这方面更多的讨论,参见Ben Cherry的著名文章《JavaScript Module Pattern: In-Depth》

                    AMD规范

                    一、模块的规范
                    先想一想,为什么模块很重要?
                    因为有了模块,我们就可以更方便地使用别人的代码,想要什么功能,就加载什么模块。

                    但是,这样做有一个前提,那就是大家必须以同样的方式编写模块,否则你有你的写法,我有我的写法,岂不是乱了套!考虑到Javascript模块现在还没有官方规范,这一点就更重要了。
                    目前,通行的Javascript模块规范共有两种:CommonJSAMD。我主要介绍AMD,但是要先从CommonJS讲起。

                    二、CommonJS
                    2009年,美国程序员Ryan Dahl创造了node.js项目,将javascript语言用于服务器端编程。

                    这标志"Javascript模块化编程"正式诞生。因为老实说,在浏览器环境下,没有模块也不是特别大的问题,毕竟网页程序的复杂性有限;但是在服务器端,一定要有模块,与操作系统和其他应用程序互动,否则根本没法编程。

                    node.js的模块系统,就是参照CommonJS规范实现的。在CommonJS中,有一个全局性方法require(),用于加载模块。假定有一个数学模块math.js,就可以像下面这样加载。
                    var math = require('math');
                    Run code
                    Cut to clipboard

                      然后,就可以调用模块提供的方法:
                        var math = require('math');   math.add(2,3); // 5
                      Run code
                      Cut to clipboard

                        因为这个系列主要针对浏览器编程,不涉及node.js,所以对CommonJS就不多做介绍了。我们在这里只要知道,require()用于加载模块就行了。

                        三、浏览器环境
                        有了服务器端模块以后,很自然地,大家就想要客户端模块。而且最好两者能够兼容,一个模块不用修改,在服务器和浏览器都可以运行。

                        但是,由于一个重大的局限,使得CommonJS规范不适用于浏览器环境。还是上一节的代码,如果在浏览器中运行,会有一个很大的问题,你能看出来吗?
                          var math = require('math');   math.add(2, 3);
                        Run code
                        Cut to clipboard

                          第二行math.add(2, 3),在第一行require('math')之后运行,因此必须等math.js加载完成。也就是说,如果加载时间很长,整个应用就会停在那里等。

                          这对服务器端不是一个问题,因为所有的模块都存放在本地硬盘,可以同步加载完成,等待时间就是硬盘的读取时间。但是,对于浏览器,这却是一个大问题,因为模块都放在服务器端,等待时间取决于网速的快慢,可能要等很长时间,浏览器处于"假死"状态。

                          因此,浏览器端的模块,不能采用"同步加载"(synchronous),只能采用"异步加载"(asynchronous)。这就是AMD规范诞生的背景。

                          四、AMD
                          AMD是"Asynchronous Module Definition"的缩写,意思就是"异步模块定义"。它采用异步方式加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。

                          AMD也采用require()语句加载模块,但是不同于CommonJS,它要求两个参数:
                          require([module], callback);
                          Run code
                          Cut to clipboard

                            第一个参数[module],是一个数组,里面的成员就是要加载的模块;第二个参数callback,则是加载成功之后的回调函数。如果将前面的代码改写成AMD形式,就是下面这样:
                              require(['math'], function (math) {     math.add(2, 3);   });
                            Run code
                            Cut to clipboard

                              math.add()与math模块加载不是同步的,浏览器不会发生假死。所以很显然,AMD比较适合浏览器环境。

                              目前,主要有两个Javascript库实现了AMD规范:require.jscurl.js

                              require.js的用法

                              一、为什么要用require.js?
                              最早的时候,所有Javascript代码都写在一个文件里面,只要加载这一个文件就够了。后来,代码越来越多,一个文件不够了,必须分成多个文件,依次加载。下面的网页代码,相信很多人都见过。
                                <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>
                              Run code
                              Cut to clipboard

                                这段代码依次加载多个js文件。

                                这样的写法有很大的缺点。首先,加载的时候,浏览器会停止网页渲染,加载文件越多,网页失去响应的时间就会越长;其次,由于js文件之间存在依赖关系,因此必须严格保证加载顺序(比如上例的1.js要在2.js的前面),依赖性最大的模块一定要放到最后加载,当依赖关系很复杂的时候,代码的编写和维护都会变得困难。

                                require.js的诞生,就是为了解决这两个问题:
                                  (1)实现js文件的异步加载,避免网页失去响应;
                                  (2)管理模块之间的依赖性,便于代码的编写和维护。

                                二、require.js的加载
                                使用require.js的第一步,是先去官方网站下载最新版本。
                                下载后,假定把它放在js子目录下面,就可以加载了。
                                <script src="js/require.js"></script>
                                Run code
                                Cut to clipboard

                                  有人可能会想到,加载这个文件,也可能造成网页失去响应。解决办法有两个,一个是把它放在网页底部加载,另一个是写成下面这样:
                                  <script src="js/require.js" defer async="true" ></script>
                                  Run code
                                  Cut to clipboard

                                    async属性表明这个文件需要异步加载,避免网页失去响应。IE不支持这个属性,只支持defer,所以把defer也写上。
                                    加载require.js以后,下一步就要加载我们自己的代码了。假定我们自己的代码文件是main.js,也放在js目录下面。那么,只需要写成下面这样就行了:
                                    <script src="js/require.js" data-main="js/main"></script>
                                    Run code
                                    Cut to clipboard

                                      data-main属性的作用是,指定网页程序的主模块。在上例中,就是js目录下面的main.js,这个文件会第一个被require.js加载。由于require.js默认的文件后缀名是js,所以可以把main.js简写成main。

                                      三、主模块的写法
                                      上一节的main.js,我把它称为"主模块",意思是整个网页的入口代码。它有点像C语言的main()函数,所有代码都从这儿开始运行。

                                      下面就来看,怎么写main.js。
                                      如果我们的代码不依赖任何其他模块,那么可以直接写入javascript代码。
                                        // main.js   alert("加载成功!");
                                      Run code
                                      Cut to clipboard

                                        但这样的话,就没必要使用require.js了。真正常见的情况是,主模块依赖于其他模块,这时就要使用AMD规范定义的的require()函数。
                                          // main.js   require(['moduleA', 'moduleB', 'moduleC'], function (moduleA, moduleB, moduleC){     // some code here   });
                                        Run code
                                        Cut to clipboard

                                          require()函数接受两个参数。第一个参数是一个数组,表示所依赖的模块,上例就是['moduleA', 'moduleB', 'moduleC'],即主模块依赖这三个模块;第二个参数是一个回调函数,当前面指定的模块都加载成功后,它将被调用。加载的模块会以参数形式传入该函数,从而在回调函数内部就可以使用这些模块。

                                          require()异步加载moduleA,moduleB和moduleC,浏览器不会失去响应;它指定的回调函数,只有前面的模块都加载成功后,才会运行,解决了依赖性的问题。

                                          下面,我们看一个实际的例子。
                                          假定主模块依赖jquery、underscore和backbone这三个模块,main.js就可以这样写:
                                            require(['jquery', 'underscore', 'backbone'], function ($, _, Backbone){     // some code here   });
                                          Run code
                                          Cut to clipboard

                                            require.js会先加载jQuery、underscore和backbone,然后再运行回调函数。主模块的代码就写在回调函数中。

                                            四、模块的加载
                                            上一节最后的示例中,主模块的依赖模块是['jquery', 'underscore', 'backbone']。默认情况下,require.js假定这三个模块与main.js在同一个目录,文件名分别为jquery.js,underscore.js和backbone.js,然后自动加载。

                                            使用require.config()方法,我们可以对模块的加载行为进行自定义。require.config()就写在主模块(main.js)的头部。参数就是一个对象,这个对象的paths属性指定各个模块的加载路径。
                                              require.config({     paths: {       "jquery": "jquery.min",       "underscore": "underscore.min",       "backbone": "backbone.min"     }   });
                                            Run code
                                            Cut to clipboard

                                              上面的代码给出了三个模块的文件名,路径默认与main.js在同一个目录(js子目录)。如果这些模块在其他目录,比如js/lib目录,则有两种写法。一种是逐一指定路径。
                                                require.config({     paths: {       "jquery": "lib/jquery.min",       "underscore": "lib/underscore.min",       "backbone": "lib/backbone.min"     }   });
                                              Run code
                                              Cut to clipboard

                                                另一种则是直接改变基目录(baseUrl)。
                                                  require.config({     baseUrl: "js/lib",     paths: {       "jquery": "jquery.min",       "underscore": "underscore.min",       "backbone": "backbone.min"     }   });
                                                Run code
                                                Cut to clipboard

                                                  如果某个模块在另一台主机上,也可以直接指定它的网址,比如:
                                                    require.config({     paths: {       "jquery": "https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min"     }   });
                                                  Run code
                                                  Cut to clipboard

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

                                                    五、AMD模块的写法
                                                    require.js加载的模块,采用AMD规范。也就是说,模块必须按照AMD的规定来写。
                                                    具体来说,就是模块必须采用特定的define()函数来定义。如果一个模块不依赖其他模块,那么可以直接定义在define()函数之中。

                                                    假定现在有一个math.js文件,它定义了一个math模块。那么,math.js就要这样写:
                                                      // math.js   define(function (){     var add = function (x,y){       return x+y;     };     return {       add: add     };   });
                                                    Run code
                                                    Cut to clipboard

                                                      加载方法如下:
                                                        // main.js   require(['math'], function (math){     alert(math.add(1,1));   });
                                                      Run code
                                                      Cut to clipboard

                                                        如果这个模块还依赖其他模块,那么define()函数的第一个参数,必须是一个数组,指明该模块的依赖性。
                                                          define(['myLib'], function(myLib){     function foo(){       myLib.doSomething();     }     return {       foo : foo     };   });
                                                        Run code
                                                        Cut to clipboard

                                                          当require()函数加载上面这个模块的时候,就会先加载myLib.js文件。

                                                          六、加载非规范的模块
                                                          理论上,require.js加载的模块,必须是按照AMD规范、用define()函数定义的模块。但是实际上,虽然已经有一部分流行的函数库(比如jQuery)符合AMD规范,更多的库并不符合。那么,require.js

                                                          是否能够加载非规范的模块呢?
                                                          回答是可以的。

                                                          这样的模块在用require()加载之前,要先用require.config()方法,定义它们的一些特征。
                                                          举例来说,underscore和backbone这两个库,都没有采用AMD规范编写。如果要加载它们的话,必须先定义它们的特征。
                                                            require.config({     shim: {       'underscore':{         exports: '_'       },       'backbone': {         deps: ['underscore', 'jquery'],         exports: 'Backbone'       }     }   });
                                                          Run code
                                                          Cut to clipboard

                                                            require.config()接受一个配置对象,这个对象除了有前面说过的paths属性之外,还有一个shim属性,专门用来配置不兼容的模块。

                                                            具体来说,每个模块要定义(1)exports值(输出的变量名),表明这个模块外部调用时的名称;(2)deps数组,表明该模块的依赖性。

                                                            比如,jQuery的插件可以这样定义:
                                                              shim: {     'jquery.scroll': {       deps: ['jquery'],       exports: 'jQuery.fn.scroll'     }   }
                                                            Run code
                                                            Cut to clipboard


                                                              七、require.js插件
                                                              require.js还提供一系列插件,实现一些特定的功能。
                                                              domready插件,可以让回调函数在页面DOM结构加载完成后再运行。
                                                                require(['domready!'], function (doc){     // called once the DOM is ready   });
                                                              Run code
                                                              Cut to clipboard

                                                                text和image插件,则是允许require.js加载文本和图片文件。
                                                                  define([     'text!review.txt',     'image!cat.jpg'     ],     function(review,cat){       console.log(review);       document.body.appendChild(cat);     }   );
                                                                Run code
                                                                Cut to clipboard

                                                                  类似的插件还有json和mdown,用于加载json文件和markdown文件。

                                                                  原文:
                                                                  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

                                                                  RequireJS和AMD规范

                                                                  概述
                                                                  RequireJS是一个工具库,主要用于客户端的模块管理。它可以让客户端的代码分成一个个模块,实现异步或动态加载,从而提高代码的性能和可维护性。它的模块管理遵守AMD规范(Asynchronous Module Definition)。

                                                                  RequireJS的基本思想是,通过define方法,将代码定义为模块;通过require方法,实现代码的模块加载。

                                                                  首先,将require.js嵌入网页,然后就能在网页中进行模块化编程了。
                                                                  <script data-main="scripts/main" src="scripts/require.js"></script>
                                                                  Run code
                                                                  Cut to clipboard

                                                                    上面代码的data-main属性不可省略,用于指定主代码所在的脚本文件,在上例中为scripts子目录下的main.js文件。用户自定义的代码就放在这个main.js文件中。

                                                                    define方法:定义模块
                                                                    define方法用于定义模块,RequireJS要求每个模块放在一个单独的文件里。

                                                                    按照是否依赖其他模块,可以分成两种情况讨论。第一种情况是定义独立模块,即所定义的模块不依赖其他模块;第二种情况是定义非独立模块,即所定义的模块依赖于其他模块。

                                                                    (1)独立模块

                                                                    如果被定义的模块是一个独立模块,不需要依赖任何其他模块,可以直接用define方法生成。
                                                                    define({ method1: function() {}, method2: function() {}, });
                                                                    Run code
                                                                    Cut to clipboard

                                                                      上面代码生成了一个拥有method1、method2两个方法的模块。

                                                                      另一种等价的写法是,把对象写成一个函数,该函数的返回值就是输出的模块。
                                                                      define(function () { return { method1: function() {}, method2: function() {}, }; });
                                                                      Run code
                                                                      Cut to clipboard

                                                                        后一种写法的自由度更高一点,可以在函数体内写一些模块初始化代码。

                                                                        值得指出的是,define定义的模块可以返回任何值,不限于对象。

                                                                        (2)非独立模块

                                                                        如果被定义的模块需要依赖其他模块,则define方法必须采用下面的格式。
                                                                        define(['module1', 'module2'], function(m1, m2) { ... });
                                                                        Run code
                                                                        Cut to clipboard

                                                                          define方法的第一个参数是一个数组,它的成员是当前模块所依赖的模块。比如,[‘module1’, ‘module2’]表示我们定义的这个新模块依赖于module1模块和module2模块,只有先加载这两个模块,新模块才能正常运行。一般情况下,module1模块和module2模块指的是,当前目录下的module1.js文件和module2.js文件,等同于写成[’./module1’, ‘./module2’]。

                                                                          define方法的第二个参数是一个函数,当前面数组的所有成员加载成功后,它将被调用。它的参数与数组的成员一一对应,比如function(m1, m2)就表示,这个函数的第一个参数m1对应module1模块,第二个参数m2对应module2模块。这个函数必须返回一个对象,供其他模块调用。
                                                                          define(['module1', 'module2'], function(m1, m2) { return { method: function() { m1.methodA(); m2.methodB(); } }; });
                                                                          Run code
                                                                          Cut to clipboard

                                                                            上面代码表示新模块返回一个对象,该对象的method方法就是外部调用的接口,menthod方法内部调用了m1模块的methodA方法和m2模块的methodB方法。

                                                                            需要注意的是,回调函数必须返回一个对象,这个对象就是你定义的模块。

                                                                            如果依赖的模块很多,参数与模块一一对应的写法非常麻烦。
                                                                            define( [ 'dep1', 'dep2', 'dep3', 'dep4', 'dep5', 'dep6', 'dep7', 'dep8'], function(dep1, dep2, dep3, dep4, dep5, dep6, dep7, dep8){ ... } );
                                                                            Run code
                                                                            Cut to clipboard

                                                                              为了避免像上面代码那样繁琐的写法,RequireJS提供一种更简单的写法。
                                                                              define( function (require) { var dep1 = require('dep1'), dep2 = require('dep2'), dep3 = require('dep3'), dep4 = require('dep4'), dep5 = require('dep5'), dep6 = require('dep6'), dep7 = require('dep7'), dep8 = require('dep8'); ... } });
                                                                              Run code
                                                                              Cut to clipboard

                                                                                下面是一个define实际运用的例子。
                                                                                define(['math', 'graph'], function ( math, graph ) { return { plot: function(x, y){ return graph.drawPie(math.randomGrid(x,y)); } } }; );
                                                                                Run code
                                                                                Cut to clipboard

                                                                                  上面代码定义的模块依赖math和graph两个库,然后返回一个具有plot接口的对象。

                                                                                  另一个实际的例子是,通过判断浏览器是否为IE,而选择加载zepto或jQuery。
                                                                                  define(('__proto__' in {} ? ['zepto'] : ['jquery']), function($) { return $; });
                                                                                  Run code
                                                                                  Cut to clipboard

                                                                                    上面代码定义了一个中间模块,该模块先判断浏览器是否支持__proto__属性(除了IE,其他浏览器都支持),如果返回true,就加载zepto库,否则加载jQuery库。

                                                                                    require方法:调用模块
                                                                                    require方法用于调用模块。它的参数与define方法类似。
                                                                                    require(['foo', 'bar'], function ( foo, bar ) { foo.doSomething(); });
                                                                                    Run code
                                                                                    Cut to clipboard

                                                                                      上面方法表示加载foo和bar两个模块,当这两个模块都加载成功后,执行一个回调函数。该回调函数就用来完成具体的任务。

                                                                                      require方法的第一个参数,是一个表示依赖关系的数组。这个数组可以写得很灵活,请看下面的例子。
                                                                                      require( [ window.JSON ? undefined : 'util/json2' ], function ( JSON ) { JSON = JSON || window.JSON; console.log( JSON.parse( '{ "JSON" : "HERE" }' ) ); });
                                                                                      Run code
                                                                                      Cut to clipboard

                                                                                        上面代码加载JSON模块时,首先判断浏览器是否原生支持JSON对象。如果是的,则将undefined传入回调函数,否则加载util目录下的json2模块。

                                                                                        require方法也可以用在define方法内部。
                                                                                        define(function (require) { var otherModule = require('otherModule'); });
                                                                                        Run code
                                                                                        Cut to clipboard

                                                                                          下面的例子显示了如何动态加载模块。
                                                                                          define(function ( require ) { var isReady = false, foobar; require(['foo', 'bar'], function (foo, bar) { isReady = true; foobar = foo() + bar(); }); return { isReady: isReady, foobar: foobar }; });
                                                                                          Run code
                                                                                          Cut to clipboard

                                                                                            上面代码所定义的模块,内部加载了foo和bar两个模块,在没有加载完成前,isReady属性值为false,加载完成后就变成了true。因此,可以根据isReady属性的值,决定下一步的动作。

                                                                                            下面的例子是模块的输出结果是一个promise对象。
                                                                                            define(['lib/Deferred'], function( Deferred ){ var defer = new Deferred(); require(['lib/templates/?index.html','lib/data/?stats'], function( template, data ){ defer.resolve({ template: template, data:data }); } ); return defer.promise(); });
                                                                                            Run code
                                                                                            Cut to clipboard

                                                                                              上面代码的define方法返回一个promise对象,可以在该对象的then方法,指定下一步的动作。

                                                                                              如果服务器端采用JSONP模式,则可以直接在require中调用,方法是指定JSONP的callback参数为define。
                                                                                              require( [ "http://someapi.com/foo?callback=define" ], function (data) { console.log(data); });
                                                                                              Run code
                                                                                              Cut to clipboard

                                                                                                require方法允许添加第三个参数,即错误处理的回调函数。
                                                                                                require( [ "backbone" ], function ( Backbone ) { return Backbone.View.extend({ /* ... */ }); }, function (err) { // ... } );
                                                                                                Run code
                                                                                                Cut to clipboard

                                                                                                  require方法的第三个参数,即处理错误的回调函数,接受一个error对象作为参数。

                                                                                                  require对象还允许指定一个全局性的Error事件的监听函数。所有没有被上面的方法捕获的错误,都会被触发这个监听函数。
                                                                                                  requirejs.onError = function (err) { // ... };
                                                                                                  Run code
                                                                                                  Cut to clipboard


                                                                                                    AMD模式小结
                                                                                                    define和require这两个定义模块、调用模块的方法,合称为AMD模式。它的模块定义的方法非常清晰,不会污染全局环境,能够清楚地显示依赖关系。

                                                                                                    AMD模式可以用于浏览器环境,并且允许非同步加载模块,也可以根据需要动态加载模块。

                                                                                                    配置require.js:config方法
                                                                                                    require方法本身也是一个对象,它带有一个config方法,用来配置require.js运行参数。config方法接受一个对象作为参数。
                                                                                                    require.config({ paths: { jquery: [ '//cdnjs.cloudflare.com/ajax/libs/jquery/2.0.0/jquery.min.js', 'lib/jquery' ] } });
                                                                                                    Run code
                                                                                                    Cut to clipboard

                                                                                                      config方法的参数对象有以下主要成员:

                                                                                                      (1)paths

                                                                                                      paths参数指定各个模块的位置。这个位置可以是同一个服务器上的相对位置,也可以是外部网址。可以为每个模块定义多个位置,如果第一个位置加载失败,则加载第二个位置,上面的示例就表示如果CDN加载失败,则加载服务器上的备用脚本。需要注意的是,指定本地文件路径时,可以省略文件最后的js后缀名。
                                                                                                      require(["jquery"], function($) { // ... });
                                                                                                      Run code
                                                                                                      Cut to clipboard

                                                                                                        上面代码加载jquery模块,因为jquery的路径已经在paths参数中定义了,所以就会到事先设定的位置下载。

                                                                                                        (2)baseUrl

                                                                                                        baseUrl参数指定本地模块位置的基准目录,即本地模块的路径是相对于哪个目录的。该属性通常由require.js加载时的data-main属性指定。

                                                                                                        (3)shim

                                                                                                        有些库不是AMD兼容的,这时就需要指定shim属性的值。shim可以理解成“垫片”,用来帮助require.js加载非AMD规范的库。
                                                                                                        require.config({ paths: { "backbone": "vendor/backbone", "underscore": "vendor/underscore" }, shim: { "backbone": { deps: [ "underscore" ], exports: "Backbone" }, "underscore": { exports: "_" } } });
                                                                                                        Run code
                                                                                                        Cut to clipboard

                                                                                                          上面代码中的backbone和underscore就是非AMD规范的库。shim指定它们的依赖关系(backbone依赖于underscore),以及输出符号(backbone为“Backbone”,underscore为“_”)。

                                                                                                          插件
                                                                                                          RequireJS允许使用插件,加载各种格式的数据。完整的插件清单可以查看官方网站

                                                                                                          下面是插入文本数据所使用的text插件的例子。
                                                                                                          define([ 'backbone', 'text!templates.html' ], function( Backbone, template ){ // ... });
                                                                                                          Run code
                                                                                                          Cut to clipboard

                                                                                                            上面代码加载的第一个模块是backbone,第二个模块则是一个文本,用’text!’表示。该文本作为字符串,存放在回调函数的template变量中。

                                                                                                            优化器r.js
                                                                                                            RequireJS提供一个基于node.js的命令行工具r.js,用来压缩多个js文件。它的主要作用是将多个模块文件压缩合并成一个脚本文件,以减少网页的HTTP请求数。

                                                                                                            第一步是安装r.js(假设已经安装了node.js)。
                                                                                                            npm install -g requirejs
                                                                                                            Run code
                                                                                                            Cut to clipboard

                                                                                                              然后,使用的时候,直接在命令行键入以下格式的命令。
                                                                                                              node r.js -o <arguments>
                                                                                                              Run code
                                                                                                              Cut to clipboard

                                                                                                                <argument>表示命令运行时,所需要的一系列参数,比如像下面这样:
                                                                                                                node r.js -o baseUrl=. name=main out=main-built.js
                                                                                                                Run code
                                                                                                                Cut to clipboard

                                                                                                                  除了直接在命令行提供参数设置,也可以将参数写入一个文件,假定文件名为build.js。
                                                                                                                  ({ baseUrl: ".", name: "main", out: "main-built.js" })
                                                                                                                  Run code
                                                                                                                  Cut to clipboard

                                                                                                                    然后,在命令行下用r.js运行这个参数文件,就OK了,不需要其他步骤了。
                                                                                                                    node r.js -o build.js
                                                                                                                    Run code
                                                                                                                    Cut to clipboard

                                                                                                                      下面是一个参数文件的范例,假定位置就在根目录下,文件名为build.js。
                                                                                                                      ({ appDir: './', baseUrl: './js', dir: './dist', modules: [ { name: 'main' } ], fileExclusionRegExp: /^(r|build)\.js$/, optimizeCss: 'standard', removeCombined: true, paths: { jquery: 'lib/jquery', underscore: 'lib/underscore', backbone: 'lib/backbone/backbone', backboneLocalstorage: 'lib/backbone/backbone.localStorage', text: 'lib/require/text' }, shim: { underscore: { exports: '_' }, backbone: { deps: [ 'underscore', 'jquery' ], exports: 'Backbone' }, backboneLocalstorage: { deps: ['backbone'], exports: 'Store' } } })
                                                                                                                      Run code
                                                                                                                      Cut to clipboard

                                                                                                                        上面代码将多个模块压缩合并成一个main.js。

                                                                                                                        参数文件的主要成员解释如下:
                                                                                                                        appDir:项目目录,相对于参数文件的位置。 baseUrl:js文件的位置。 dir:输出目录。 modules:一个包含对象的数组,每个对象就是一个要被优化的模块。 fileExclusionRegExp:凡是匹配这个正则表达式的文件名,都不会被拷贝到输出目录。 optimizeCss: 自动压缩CSS文件,可取的值包括“none”, “standard”, “standard.keepLines”, “standard.keepComments”, “standard.keepComments.keepLines”。 removeCombined:如果为true,合并后的原文件将不保留在输出目录中。 paths:各个模块的相对路径,可以省略js后缀名。 shim:配置依赖性关系。如果某一个模块不是AMD模式定义的,就可以用shim属性指定模块的依赖性关系和输出值。 generateSourceMaps:是否要生成source map文件。
                                                                                                                        Run code
                                                                                                                        Cut to clipboard

                                                                                                                          更详细的解释可以参考官方文档

                                                                                                                          运行优化命令后,可以前往dist目录查看优化后的文件。

                                                                                                                          下面是另一个build.js的例子。
                                                                                                                          ({ mainConfigFile : "js/main.js", baseUrl: "js", removeCombined: true, findNestedDependencies: true, dir: "dist", modules: [ { name: "main", exclude: [ "infrastructure" ] }, { name: "infrastructure" } ] })
                                                                                                                          Run code
                                                                                                                          Cut to clipboard

                                                                                                                            上面代码将模块文件压缩合并成两个文件,第一个是main.js(指定排除infrastructure.js),第二个则是infrastructure.js。

                                                                                                                            参考链接
                                                                                                                            NaorYe, Optimize (Concatenate and Minify) RequireJS Projects
                                                                                                                            Jonathan Creamer, Deep dive into Require.js
                                                                                                                            Addy Osmani, Writing Modular JavaScript With AMD, CommonJS & ES Harmony
                                                                                                                            Jim Cowart, Five Helpful Tips When Using RequireJS
                                                                                                                            Jim Cowart, Using r.js to Optimize Your RequireJS Project

                                                                                                                            原文链接
                                                                                                                            http://javascript.ruanyifeng.com/tool/requirejs.html