js模塊化開發——require.js的用法詳細介紹(含jsonp)

RequireJS的目標是鼓勵代碼的模塊化,它使用了不一樣於傳統<script>標籤腳本加載步驟。能夠用它回事、優化代碼,但其主要的目的仍是爲了代碼的模塊化。它鼓勵在使用腳本以moudle ID替代URL地址。javascript

RequireJS以一個相對於baseUrl的地址來加載全部的代碼。頁面頂層<script>標籤含有一個特殊的屬性data-main,require.js使用它來啓動腳本加載過程,而baseUrl通常設置到與該屬性相一致的目錄。下列示例中展現了baseUrl的設置。css

     <!—將baseUrl設置到」scripts」目錄,而且加載module ID 爲’main’的一個腳本--->html

      <script data-main=」script/main.js」 src= 「scripts/require.js」></script>java

baseUrl也能夠經過RequrieJS config拖動設置。若是沒有顯示指定config及data-main,則默認的baseUrl爲包含RequireJS的html頁面所在目錄。node

requireJS默認假定全部的依賴資源都是js腳本,所以無需在module ID上加載」.js」後綴,RequireJS在進行module ID到path的解析時會自動補上後綴。你能夠經過paths config設置一組腳本,這些有助於咱們在使用腳本時寫更少的字。jquery

有時候你想避開「baseUrl+paths」的解析過程,而是直接指定加載某一個目錄下的腳本。此時能夠這樣作:若是一個module ID符合下述規則之一,其ID解析會避開常規的的「baseUrl+paths」配置,而是直接將其加載爲一個相對於當前html文檔的腳本:git

n  以「.js」結尾;github

n  以「/」開頭;web

n  包含URL協議,「http:」,」https:」;ajax

通常來講,最好仍是使用baseUrl及」path」 config去設置module ID。它會給你帶來額外靈活性,如便於腳本的重命名、重定位。同時,爲了不凌亂的配置,最好不要使用多級嵌套目錄層次來組織代碼,而是要麼將全部的腳本 都放置到baseUrl中,要麼分置爲項目庫/第三方庫的一個扁平結構,以下:

www/

      index.html

      js/

        app/

              sub.js

         lib/

              jquery.js

              canvas.js

         app.js

         require.js

在index.html中:

   <script data-main=」js/app.js」 src=」js/require.js」></script>

在app.js中:

    requirejs.config({

            //默認從js/lib加載全部的module ID

            baseUrl:’js/lib’,

           //除非,module ID以「app」開關,不然從js/app目錄加載。

           //注意path,config是相對於baseUrl的,

           //並且不要包含」.js」的後綴,由於一個path

           //有多是個目標

           paths:{

                   app:’../app’

            }

    });

    //啓動main app

     requirejs([‘jquery’,’canvas’,’app/sub’],function($,canvas,sub){

           自此,jQuery,canvas以及app/sub模塊都已加載並開始使用了

     });

 

注意在示例中,三方庫如jQuery沒有將版本號包含在他們的文件名中。咱們建議版本信息放置在單獨的文件中來進行跟蹤。使用諸如volo這類的工 具,能夠將package.json打上版本信息,並在磁盤上保持文件我爲「jquery.js」。這有助於你保持配置的最小化,避免爲每一個庫版本設置一 條path。

理想情況下,每一個加載的腳本都是經過define()來定義的一個模塊;但這些「瀏覽器全局變量注入」型的傳統/遺留庫並無使用define()來定義它們的依賴關係,你必須爲此使用shim config來指明它們的依賴關係。若是你沒有指明依賴關係,加載可能報錯。這是由於基於速度的緣由,RequireJS會異步地以無序的形式加載這些庫。

定義模塊

模塊不一樣於傳統的腳本文件,它良好地定義了一個做用域避免全局名稱空間的污染。它能夠顯式地列出其依賴關係,並以函數參數的形式將這些依賴進行注 入,而無需引用全局變量。RequireJS的模塊是模塊模式的一個擴展,其好處是無需全局地引用其它模塊。RequireJS的模塊語法容許它儘快地加 載多個模塊,雖然加載的順序不定,但依賴的順序最終是正確的。同時由於無需建立全局變量,甚至能夠作到在同一個頁面上同時加載同一模塊的不一樣版本。

多個模塊可使用內置優化工具(optimization)將其組織打包。

簡單的值對

define({color:」block」,size:」unisize」});

函數式定義

若是一個模塊沒有任何依賴,可是須要一個作setup工做的函數,則在define()中定義該函數,並將其傳給define():

    define(function(){

     //Do setup work here

       return {

            color:」black」,

            size:」unisize」

       }

    })

存在依賴的函數式定義

若是模塊存在依賴:則第一個參數是依賴的名稱數組;第二個參數是函數,在模塊的全部依賴加載完畢後,該函數會被調用來定義該模塊,所以該模塊應該返回一個定義了本模塊的object。依賴關係會以參數的形式注入到該函數上,參數列表與依賴名稱列表-----對應。

   //my/shirt.js如今對同目錄下的cart及inventory存在依賴

   define([‘./cart’,’./inventory’],function(cart,inventory){

          //返回一個定義了該」my/shirt」模塊的object

           retrun {

                  color:」blue」,

                  size:」large」,

                  addToCart:function(){

                        inventory.decrement(this);

                         cart.add(this)

                  }

           }

   });

 

嚴重不鼓勵模塊定義全局變量。遵循此處的定義模式,可使得同一模塊的不一樣版本並存於同一個頁頁面上。另外,參數的順序與依賴順序保存一致。返回的object定義」my/shirt」模塊。這種定義模式下,」my/shirt」不做爲一個全局變量而存在。

 

將模塊定義爲一個函數

對模塊的返回值類型並無強制爲必定是個object,任何函數的返回值都是容許。此處是一個返回了函數的模塊定義:

   //foo/title.js像以前同樣使用my/cart及my/inventory模塊

   //但foo/bar.js位於不一樣於」my」模塊的目錄下,它在模塊依賴名稱

   //中使用」my」來定位它們。依賴名稱中的」my」可能映射到任意一個目錄,

   //但默認,假定它鄰接着」foo」目錄

     define([「my/cart」,」my/inventory」],function(cart,inventory){

          //返回一個函數以定義」foo/title」

          //它獲取/設置window的title

             return function(title){

                          returntitle?(window.title=title):inventory.storeName+」  「+cart.name;

             }

      });

 

 

簡單包裝CommonJS來定義模塊

若是你如今有一些以CommonJS模塊格式編寫的代碼,而這些代碼難於使用上述依賴名稱數組參數的形式來重構,你能夠考慮直接將這些依賴對應到一些本地變量中進行使用。你可使用一個CommonJS的簡單包裝來實現:

    define(function(require,exports,module){

        var a = require(‘a’), b = require(‘b’);

         //Return the module value

         return function(){};

    });

該包裝方法依靠Function.prototype.toString()將函數內容賦予一個有意義的字符串,但在一些設備如ps3及一些老的Opera手機瀏覽器中不起做用。考慮在這些設備上使用優化器將依賴導出爲數組形式。

 

定義一個命名模塊

你可能會看到一些define()中包含一個模塊名稱做爲首個參數:

    define(「foo/title」, [「my/cart」,」my/inventory」],function(cart,inventory){

               //此處定義foo/title object

      })

這些常由優化工具生成。你也能夠本身顯式指定模塊名稱,但這使模塊更不具有行移植性----就是說你惹將文件移動到其它目錄下,你就得重命名。通常最好避免模塊硬編碼,而交給優化工具去生成。優化工具須要生成模塊名以將多個模塊打成一個包,加快到瀏覽器的載入速度。

其餘注意事項

一個文件一個模塊:每一個js文件應該只定義一個模塊,這是模塊名到文件名查找機制的天然要求。多個模塊會被優化工具組織優化,但你在使用優化工具時應將多個模塊放置到一個文件中。

define()中的相對模塊(注意是模塊)名:爲了能夠在define()內部使用諸如require(「./relative/name」)的調用以正確解析相對名稱,記得將」require」自己做爲一個依賴注入到模塊中:

    define([「require」,」./relative/name」],function(require){

              var mod = require(」./relative/name」);

     });

或者更好地,使用下述爲轉換CommonJS模塊所設置的更短的語法:

    define(function(require){

          var mod = require(」./relative/name」);

    });

該形式利用了Function.prototype.toString()去查找require()調用,而後將其與」require」一塊兒加入到依賴數組中,這樣的代碼能夠正確地解析相對路徑了。

生成相對於模塊的URL地址:你可能須要生成一個相對於模塊的URL地址。你能夠將」require」做爲一個依賴注入進來,而後調用require.toUrl()以生成該URL:

    define([「require」],function(require){

          var cssUrl = require.toUrl(「./style.css」);

    })

控制檯調式:若是你須要處理一個已經過require([「module/name」],function(){})調用加載了的模塊,可使用模塊名做爲字符串參數的require()調用來獲取它:

    require(「module/name」).callSomeFunction() 注意這種形式僅在」module/name」已經由其異步形式的require([「module/name」])加載後纔有效。只能在define內部 使用形如」./module/name」的相對路徑。

 

循環依賴

若是你定義了一個循環依賴(a依賴b,b同時依賴a),則在這種情形下當b的模塊函數被調用的時候,它會獲得一個undefined的a。b能夠在模塊已經定義好後用require()方法不規則獲取(記得require做爲依賴注入進來)

    //b.js:

      define([「require」,」a」],

                  function(require,a){

                       //」a」將是null,若是a/b間是循環依賴

                           returnfunction(title){

                                      return require(「a」).doSomething();

                           }

                   }

     );

通常狀況下你無需使用require()方法獲取一個模塊,而是應當使用注入到模塊函數參數中的依賴。循環依賴比較罕見,它也是一個重構從新設計的警示燈。但無論怎樣,有時候仍是要用到循環依賴,這種情形下就使用上述的require()方式來解決。

若是你熟悉CommonJS,你能夠考慮使用exports爲模塊創建一個空object,該object能夠當即被其餘模塊引用。在循環依賴的兩 頭都如引操做以後,你就能夠安全持有其餘模塊了。這種方法僅在每一個模塊都是輸出object做爲模塊值的時候有效,換成函數效。

//b.js:

define(function(require,exports, module) {

    //若"a"使用了exports,則此處咱們就擁有了一個真正的object引用。

    //但在b返回值以前咱們沒法使用a的任何屬性。

    var a = require("a");

 

    exports.foo = function () {

        return a.bar();

    };

});

或者,若是你使用依賴注入數組的步驟,則可用注入特殊的"exports"來解決:

//b.js:

define(['a','exports'], function(a, exports) {

    //若"a"使用了exports,則此處咱們就擁有了一個真正的object引用。

    //但在b返回值以前咱們沒法使用a的任何屬性。

 

    exports.foo = function () {

        return a.bar();

    };

});

JSONP服務依賴

JSONP是在javascript中服務調用的一種方式。它僅需簡單地經過一個script標籤發起http get請求,是實現跨域調用的一種公認手段。爲了在RequireJS中使用JSON服務,需要將callback參數的值指定爲「define」。這意 味着你能夠獲取到的JSONP URL的值當作是一個模塊定義。

下面是一個調用JSONP API端點的示例。該示例中,JSONP的callback參數爲」callback」,所以」callback=define」告訴API將JSON響應包裹到一個」define()」中:

require(["http://example.com/api/data.json?callback=define"],

    function (data) {

        //data將做爲此條JSONP data調用的API響應

        console.log(data);

    }

);

JSONP的這種用法應僅限於應用的初始化中,一旦JSONP服務超時,其餘經過define()定義了的模塊也可能得不到執行,錯誤處理不是十分健壯。

僅支持返回值類型爲json objectJSONP服務,其餘返回類型如數組、字串、數字等都不能支持。這種功能不應用於long-polling類的jsonp鏈接即那些用來處理實時流的API。這些API在接收到響應後通常會作script的清理,而RequireJS則只能獲取該JSONP URL一次,後繼使用require()或define()發起的對同一URL的依賴(請求)只會獲得一個緩存過的值。

undefine一個模塊

有一個全局函數requirejs.undef()用來undefine一個模塊。它會重置loader的內部狀態以使其忘記以前定義的一個模塊。

可是如有其餘模塊已將此模塊做爲依賴使用了,該模塊就不會被清除,因此該功能僅在無其餘模塊持有該模塊時錯誤處理中,或者當將來須要加載該模塊時有點有。

 

機理

RequireJS使用head.appendChild()將每個依賴加載爲一個script標籤。RequireJS等待全部依賴加載完畢, 計算出模塊定義函數正確調用順序,而後依次調用它們。在同步加載的服務器端javascript環境中,可簡單地重定義require.load()來使 用RequireJS。build系統就是這麼作的。該環境中的require.load實現可在build/jslib /requirePatch.js中找到。

將來可能將該部分代碼置入require/目錄下做爲一個可選模塊,這樣你能夠在你的宿主環境中使用它來得到正確的加載順序。

配置項

當在頂層html頁面(或不做爲一個模塊定義的頂層腳本文件)中,可將配置做爲首項放入:

<scriptsrc="scripts/require.js"></script>

<script>

  require.config({

    baseUrl: "/another/path",

    paths: {

        "some": "some/v1.0"

    },

    waitSeconds: 15

  });

  require( ["some/module","my/module", "a.js", "b.js"],

    function(someModule,myModule) {

        //該函數會在上述全部的依賴加載完畢後調用。

        //注意該函數可在頁面加載完畢前被調用。

        //本回調函數是可選的。

    }

  );

</script>

或者,你將配置做爲全局變量「require」在require.js加載以前進行定義,它會被自動應用。下面的示例定義的依賴會在require.js一旦定義了require()以後即被加載

<script>

    var require = {

        deps: ["some/module1","my/module2", "a.js", "b.js"],

        callback: function(module1, module2) {

            //該函數會在上述全部的依賴加載完畢後調用。

            //注意該函數可在頁面加載完畢前被調用。

            //本回調函數是可選的。

        }

    };

</script>

<scriptsrc="scripts/require.js"></script>

注意:最好使用 var require = {}的形式而不是 window.require = {}的形式。後者在IE中運行不正常。

支持的配置項:

 

baseUrl :全部模塊的查找根路徑。因此上面的示例中,"my/module"的標籤src值是"/another/path/my/module.js"。當加載 純.js文件(依賴字串以/開頭,或者以.js結尾,或者含有協議),不會使用baseUrl。所以a.js及b.js都在包含上述代碼段的HTML頁面 的同目錄下加載。

如未顯式設置baseUrl,則默認值是加載require.js的HTML所處的位置。若是用了data-main屬性,則該路徑就變成baseUrl。

baseUrl可跟require.js頁面處於不一樣的域下,RequireJS腳本的加載是跨域的。惟一的限制是使用text!plugins加 載文本內容時,這些路徑應跟頁面同域,至少在開發時應這樣。優化工具會將text! plugin資源內聯,所以在使用優化工具以後你可使用跨域引用text!plugin資源的那些資源。

 

paths :path映射那些不直接放置於baseUrl下的模塊名。設置path時起始位置是相對於baseUrl的,除非該path設置以"/"開頭或含有 URL協議(如http:)。在上述的配置下,"some/module"的script標籤src值是"/another/path/some /v1.0/module.js"。

用於模塊名的path不該含有.js後綴,由於一個path有可能映射到一個目錄。路徑解析機制會自動在映射模塊名到path時添加上.js後綴。在文本模版之類的場景中使用require.toUrl()時它也會添加合適的後綴。

在瀏覽器中運行時,可指定路徑的備選(fallbacks),以實現諸如首先指定了從CDN中加載,一旦CDN加載失敗則從本地位置中加載這類的機制。

shim: 爲那些沒有使用define()來聲明依賴關係、設置模塊的"瀏覽器全局變量注入"型腳本作依賴和導出配置。

下面有個示例,它須要 RequireJS2.1.0+,而且假定backbone.js、underscore.js 、jquery.js都裝於baseUrl目錄下。若是沒有,則你可能須要爲它們設置pathsconfig:

      requirejs.config({

    shim: {

        'backbone': {

            //下述依賴腳本應在backbone.js以前加載

            deps:['underscore', 'jquery'],

            //一旦加載,使用全局變量'Backbone'做爲模塊值

            exports:'Backbone'

        },

        'underscore': {

            exports: '_'

        },

        'foo': {

            deps: ['bar'],

            exports: 'Foo',

            init: function (bar) {

                //使用該函數容許你調用庫所支持的noConflict方法,或其餘的清理工做。

                //可是這些庫的一些插件們可能依然須要一個全局引用,函數中的"this"提供這個全局引用。

                //依賴會以函數參數的形式被注入。

                //若是本函數具有返回值, 則該值會被用作模塊的export值,而不是使用上述'exports'中的字串。

                return this.Foo.noConflict();

            }

        }

    }

});

 

//而後,在一個單獨的文件中,如'MyModel.js',定義一個模塊,

//指定'backbone'做爲依賴。RequireJS會使用shim配置去合理

//地加載'backbone'並給予該模塊一個本地的引用。全局的Backbone引用一併

//存在於頁面上。

define(['backbone'],function (Backbone) {

  return Backbone.Model.extend({});

});

RequireJS 2.0.*中,shim配置中的"exports"屬性能夠是一個函數而不是字串。這種狀況下它就起到上述示例中的"init"屬性的功能。 RequireJS 2.1.0+中加入了"init"承接庫加載後的初始工做,以使exports做爲字串值被enforceDefine所使用。

 

那些僅做爲jQueryBackbone的插件存在而不導出任何模塊變量的"模塊"們,shim配置可簡單設置爲依賴數組:

 

requirejs.config({

    shim: {

        'jquery.colorize': ['jquery'],

        'jquery.scroll': ['jquery'],

        'backbone.layoutmanager': ['backbone']

    }

});

但請注意,若你想在IE中使用404加載檢測以啓用path備選(fallbacks)或備錯(errbacks),則須要給定一個字串值的exports以使loader可以檢查出腳本是否實際加載了(init中的返回值不會用於enforceDefine檢查中):

 

requirejs.config({

    shim: {

        'jquery.colorize': {

            deps: ['jquery'],

            exports: 'jQuery.fn.colorize'

        },

        'jquery.scroll': {

            deps: ['jquery'],

            exports: 'jQuery.fn.scroll'

        },

        'backbone.layoutmanager': {

            deps: ['backbone']

            exports: 'Backbone.LayoutManager'

        }

    }

});

"shim"配置的重要注意事項:

shim配置僅設置了代碼的依賴關係,想要實際加載shim指定的或涉及的模塊,仍然須要一個常規的require/define調用。設置shim自己不會觸發代碼的加載

請僅使用其餘"shim"模塊做爲shim腳本的依賴,或那些沒有依賴關係,而且在調用define()以前定義了全局變量(如jQuery或 lodash)的AMD庫。不然,若是你使用了一個AMD模塊做爲一個shim配置模塊的依賴,在build以後,AMD模塊可能在shim託管代碼執行 以前都不會被執行,這會致使錯誤。終極的解決方案是將全部shim託管代碼都升級爲含有可選的AMD define()調用。

"shim"配置的優化器重要注意事項:

您應當使用 mainConfigFile build配置項來指定含有shim配置的文件位置,不然優化器不會知曉shim配置。另外一個手段是將shim配置複製到build profile中。

不要在一個build中混用CDN加載和shim配置。示例場景,如:你從CDN加載jQuery的同時使用shim配置加載依賴於jQuery的 原版Backbone。不要這麼作。您應該在build中將jQuery內聯而不是從CDN加載,不然build中內聯的Backbone會在CDN加載 jQuery以前運行。這是由於shim配置僅延時加載到全部的依賴已加載,而不會作任何define的自動裝裹(auto-wrapping)。在 build以後,全部依賴都已內聯,shim配置不能延時執行非define()的代碼。define()的模塊能夠在build以後與CDN加載代碼一 並工做,由於它們已將本身的代碼合理地用define裝裹了,在全部的依賴都已加載以前不會執行。所以記住:shim配置僅是個處理非模塊(non- modular)代碼、遺留代碼的將就手段,如能夠應儘可能使用define()的模塊。

對於本地的多文件build,上述的CDN加載建議仍然適用。任何shim過的腳本,它們的依賴必須加載於該腳本執行以前。這意味着要麼直接在含有 shim腳本的build層build它的依賴,要麼先使用require([], function (){})調用來加載它的依賴,而後對含有shim腳本的build層發出一個嵌套的require([])調用。

若是您使用了uglifyjs來壓縮代碼,不要將uglify的toplevel選項置爲true,或在命令行中不要使用 -mt。 該選項會破壞shim用於找到exports的全局名稱。

map: 對於給定的模塊前綴,使用一個不一樣的模塊ID來加載該模塊。

該手段對於某些大型項目很重要:若有兩類模塊須要使用不一樣版本的"foo",但它們之間仍須要必定的協同。

在那些基於上下文的多版本實現中很難作到這一點。並且,paths配置僅用於爲模塊ID設置root paths,而不是爲了將一個模塊ID映射到另外一個。

map示例:

 

requirejs.config({

    map: {

        'some/newmodule': {

            'foo': 'foo1.2'

        },

        'some/oldmodule': {

            'foo': 'foo1.0'

        }

    }

});

若是各模塊在磁盤上分佈以下:

 

foo1.0.js

foo1.2.js

some/

newmodule.js

oldmodule.js

當「some/newmodule」調用了「require('foo')」,它將獲取到foo1.2.js文件;而當「some/oldmodule」調用「`require('foo')」時它將獲取到foo1.0.js。

 

該特性僅適用於那些調用了define()並將其註冊爲匿名模塊的真正AMD模塊腳本。而且,請在map配置中僅使用絕對模塊ID,「../some/thing」之類的相對ID不能工做。

另外在map中支持「*」,意思是「對於全部的模塊加載,使用本map配置」。若是還有更細化的map配置,會優先於「*」配置。示例:

 

requirejs.config({

    map: {

        '*': {

            'foo': 'foo1.2'

        },

        'some/oldmodule': {

            'foo': 'foo1.0'

        }

    }

});

意思是除了「some/oldmodule」外的全部模塊,當要用「foo」時,使用「foo1.2」來替代。對於「some/oldmodule」本身,則使用「foo1.0」。

 

config:經常須要將配置信息傳給一個模塊。這些配置每每是application級別的信息,須要一個手段將它們向下傳遞給模塊。在 RequireJS中,基於requirejs.config()的config配置項來實現。要獲取這些信息的模塊能夠加載特殊的依賴 「module」,並調用module.config()。示例:

 

requirejs.config({

    config: {

        'bar': {

            size: 'large'

        },

        'baz': {

            color: 'blue'

        }

    }

});

 

//bar.js用了最簡單的CJS裝裹:

//http://requirejs.org/docs/whyamd.html#sugar

define(function(require, exports, module) {

    //其值是'large'

    var size = module.config().size;

});

 

//baz.js使用了一個依賴數組,

//並要求一個特殊的依賴「module」:

//https://github.com/jrburke/requirejs/wiki/Differences-between-the-simplified-CommonJS-wrapper-and-standard-AMD-define#wiki-magic

define(['module'],function (module) {

    //Will be the value 'blue'

    var color = module.config().color;

});

若要將config傳給包,將目標設置爲包的主模塊而不是包ID:

 

requirejs.config({

    //將API key傳遞給包的主模塊:

    config: {

        'pixie/index': {

            apiKey: 'XJKDLNS'

        }

    },

    //設置「pixie」包的主模塊爲pixie目錄下的index.js

    packages: [

        {

            name: 'pixie',

            main: 'index'

        }

    ]

});

packages:從CommonJS包(package)中加載模塊。參見從包中加載模塊。

 

waitSeconds:在放棄加載一個腳本以前等待的秒數。設爲0禁用等待超時。默認爲7秒。

 

context:命名一個加載上下文。這容許require.js在同一頁面上加載模塊的多個版本,若是每一個頂層require調用都指定了一個惟一的上下文字符串。想要正確地使用,請參考多版本支持一節。

 

deps:指定要加載的一個依賴數組。當將require設置爲一個config object在加載require.js以前使用時頗有用。一旦require.js被定義,這些依賴就已加載。使用deps就像調用 require([]),但它在loader處理配置完畢以後就當即生效。它並不阻塞其餘的require()調用,它僅是指定某些模塊做爲config 塊的一部分而異步加載的手段而已。

 

callback:在deps加載完畢後執行的函數。當將require設置爲一個config object在加載require.js以前使用時頗有用,其做爲配置的deps數組加載完畢後爲require指定的函數。

 

enforceDefine:若是設置爲true,則當一個腳本不是經過define()定義且不具有可供檢查的shim導出字串值時,就會拋出錯誤。參考在IE中捕獲加載錯誤一節。

 

xhtml:若是設置爲true,則使用document.createElementNS()去建立script元素。

 

urlArgs:RequireJS獲取資源時附加在URL後面的額外的query參數。做爲瀏覽器或服務器未正確配置時的「cache bust」手段頗有用。使用cache bust配置的一個示例:

urlArgs: "bust=" +  (new Date()).getTime()

在開發中這頗有用,但請記得在部署到生產環境以前移除它。

 

scriptType:指定RequireJS將script標籤插入document時所用的type=""值。默認爲「text/javascript」。想要啓用Firefox的JavaScript 1.8特性,可以使用值「text/javascript;version=1.8」。

 

進階應用

從包中加載模塊

RequireJS支持從CommonJS包結構中加載模塊,但須要一些額外的配置。具體地,支持以下的CommonJS包特性:

一個包能夠關聯一個模塊名/前綴。

package config可爲特定的包指定下述屬性:

name:包名(用於模塊名/前綴映射)

location: 磁盤上的位置。位置是相對於配置中的baseUrl值,除非它們包含協議或以「/」開頭

main:當以「包名」發起require調用後,所應用的一個包內的模塊。默認爲「main」,除非在此處作了另外設定。該值是相對於包目錄的。

重要事項:

 

雖然包能夠有CommonJS的目錄結構,但模塊自己應爲RequireJS可理解的模塊格式。例外是:若是你在用r.js Node適配器,模塊能夠是傳統的CommonJS模塊格式。你可使用CommonJS轉換工具來將傳統的CommonJS模塊轉換爲 RequireJS所用的異步模塊格式。

一個項目上下文中僅能使用包的一個版本。你可使用RequireJS的多版本支持來加載兩個不一樣的模塊上下文;但若你想在同一個上下文中使用依賴了不一樣版本的包C的包A和B,就會有問題。將來可能會解決此問題。

若是你使用了相似於入門指導中的項目佈局,你的web項目應大體以以下的佈局開始(基於Node/Rhino的項目也是相似的,只不過使用scripts目錄中的內容做爲項目的頂層目錄):

 

project-directory/

project.html

scripts/

require.js

而下面的示例中使用了兩個包,cart及store:

 

project-directory/

project.html

cart/

main.js

store/

main.js

util.js

main.js

require.js

project.html會有以下的一個script標籤:

 

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

這會指示require.js去加載scripts/main.js。main.js使用「packages」配置項來設置相對於require.js的各個包,此例中是源碼包「cart」及「store」:

 

//main.js的內容

//傳遞一個config object到require

require.config({

    "packages": ["cart","store"]

});

 

require(["cart","store", "store/util"],

function(cart,   store,   util) {

    //正常地使用模塊

});

對「cart」的依賴請求會從scripts/cart/main.js中加載,由於「main」是RequireJS默認的包主模塊。對「store/util」的依賴請求會從scripts/store/util.js加載。

 

若是「store」包不採用「main.js」約定,以下面的結構:

 

project-directory/

project.html

scripts/

cart/

main.js

store/

store.js

util.js

main.js

package.json

require.js

則RequireJS的配置應以下:

 

require.config({

    packages: [

        "cart",

        {

            name: "store",

            main: "store"

        }

    ]

});

減小麻煩期間,強烈建議包結構聽從「main.js」約定。

 

多版本支持

如配置項一節中所述,能夠在同一頁面上以不一樣的「上下文」配置項加載同一模塊的不一樣版本。require.config()返回了一個使用該上下文配置的require函數。下面是一個加載不一樣版本(alpha及beta)模塊的示例(取自test文件中):

 

<scriptsrc="../require.js"></script>

<script>

var reqOne =require.config({

  context: "version1",

  baseUrl: "version1"

});

 

reqOne(["require","alpha", "beta",],

function(require,   alpha,  beta) {

  log("alpha version is: " +alpha.version); //prints 1

  log("beta version is: " +beta.version); //prints 1

 

  setTimeout(function() {

    require(["omega"],

      function(omega) {

        log("version1 omega loaded withversion: " +

             omega.version); //prints 1

      }

    );

  }, 100);

});

 

var reqTwo =require.config({

      context: "version2",

      baseUrl: "version2"

    });

 

reqTwo(["require","alpha", "beta"],

function(require,   alpha,  beta) {

  log("alpha version is: " +alpha.version); //prints 2

  log("beta version is: " +beta.version); //prints 2

 

  setTimeout(function() {

    require(["omega"],

      function(omega) {

        log("version2 omega loaded withversion: " +

            omega.version); //prints 2

      }

    );

  }, 100);

});

</script>

注意「require」被指定爲模塊的一個依賴,這就容許傳遞給函數回調的require()使用正確的上下文來加載多版本的模塊。若是「require」沒有指定爲一個依賴,則極可能會出現錯誤。

 

在頁面加載以後加載代碼

上述多版本示例中也展現瞭如何在嵌套的require()中遲後加載代碼。

 

對Web Worker的支持

從版本0.12開始,RequireJS可在Web Worker中運行。能夠經過在web worker中調用importScripts()來加載require.js(或包含require()定義的JS文件),而後調用require就行了。

 

你可能須要設置baseUrl配置項來確保require()可找到待加載腳本。

你能夠在unit test使用的一個文件中找到一個例子。

 

對Rhino的支持

RequireJS可經過r.js適配器用在Rhino中。參見r.js的README。

 

處理錯誤

一般的錯誤都是404(未找到)錯誤,網絡超時或加載的腳本含有錯誤。RequireJS有些工具來處理它們:require特定的錯誤回調(errback),一個「paths」數組配置,以及一個全局的requirejs.onError事件。

 

傳入errback及requirejs.onError中的error object一般包含兩個定製的屬性:

 

requireType:含有類別信息的字串值,如「timeout」,「nodefine」, 「scripterror」

requireModules: 超時的模塊名/URL數組。

若是你獲得了requireModules錯,可能意味着依賴於requireModules數組中的模塊的其餘模塊未定義。

 

 在IE中捕獲加載錯

 

Internet Explorer有一系列問題致使檢測errbacks/paths fallbacks中的加載錯 比較困難:

 

IE 6-8中的script.onerror無效。沒有辦法判斷是否加載一個腳本會致使404錯;更甚地,在404中依然會觸發state爲complete的onreadystatechange事件。

IE 9+中script.onerror有效,但有一個bug:在執行腳本以後它並不觸發script.onload事件句柄。所以它沒法支持匿名AMD模塊 的標準方法。因此script.onreadystatechange事件仍被使用。可是,state爲complete的 onreadystatechange事件會在script.onerror函數觸發以前觸發。

所以IE環境下很難一箭雙鵰:匿名AMD(AMD模塊機制的核心優點)和可靠的錯誤檢測。

但若是你的項目裏使用了define()來定義全部模塊,或者爲其餘非define()的腳本使用shim配置指定了導出字串,則若是你將 enforceDefine配置項設爲true,loader就能夠經過檢查define()調用或shim全局導出值來確認腳本的加載無誤。

所以若是你打算支持Internet Explorer,捕獲加載錯,並使用了define()或shim,則記得將enforceDefine設置爲true。參見下節的示例。

 

注意:若是你設置了enforceDefine: true,並且你使用data-main=""來加載你的主JS模塊,則該主JS模塊必須調用define()而不是require()來加載其所需的代 碼。主JS模塊仍然可調用require/requirejs來設置config值,但對於模塊加載必須使用define()。

若是你使用了almond而不是require.js來build你的代碼,記得在build配置項中使用insertRequire來在主模塊中插入一個require調用 —— 這跟data-main的初始化require()調用起到相同的目的。

 

require([]) errbacks

 

當與requirejs.undef()一同使用errback時,容許你檢測模塊的一個加載錯,而後undefine該模塊,並重置配置到另外一個地址來進行重試。

一個常見的應用場景是先用庫的一個CDN版本,若是其加載出錯,則切換到本地版本:

 

requirejs.config({

    enforceDefine: true,

    paths: {

        jquery:'http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min'

    }

});

 

//Later

require(['jquery'],function ($) {

    //使用$

}, function (err){

    //errback

    //error含有出錯的模塊列表

    var failedId = err.requireModules&& err.requireModules[0],

    if (failedId === 'jquery') {

        //undef是全局的requirejs object上的一個函數。

        //用它來清空jQuery的信息。任何依賴於jQuery或處於加載中的模塊都再也不

        //加載,它們會等待有效的jQuery加載完畢。

        requirejs.undef(failedId);

 

        //將jQuery設置到本地版本上

        requirejs.config({

            paths: {

                jquery: 'local/jquery'

            }

        });

 

        //重試。注意上述含有「使用$」一句的require回調會在新的

        //jQuery加載成功後被調用。

        require(['jquery'], function () {});

    } else {

        //其餘錯。考慮報錯給用戶。

    }

});

使用「requirejs.undef()」,若是你配置到不一樣的位置並從新嘗試加載同一模塊,則loader會將依賴於該模塊的那些模塊記錄下來並在該模塊從新加載成功後去加載它們。

 

注意:errback僅適用於回調風格的require調用,而不是define()調用。define()僅用於聲明模塊。

 paths備錯配置

 

上述模式(檢錯,undef()模塊,修改paths,重加載)是一個常見的需求,所以有一個快捷設置方式。paths配置項容許數組值:

 

requirejs.config({

    //爲了在IE中正確檢錯,強制define/shim導出檢測

    enforceDefine: true,

    paths: {

        jquery: [

           'http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min',

            //若CDN加載錯,則從以下位置重試加載

            'lib/jquery'

        ]

    }

});

 

//後面

require(['jquery'],function ($) {

});

上述代碼先嚐試加載CDN版本,若是出錯,則退回到本地的lib/jquery.js。

 

注意:paths備錯僅在模塊ID精確匹配時工做。這不一樣於常規的paths配置,常規配置可匹配模塊ID的任意前綴部分。備錯主要用於很是的錯誤恢復,而不是常規的path查找解析,由於那在瀏覽器中是低效的。

全局的 requirejs.onError

 

爲了捕獲在局域的errback中未捕獲的異常,你能夠重載requirejs.onError():

 

requirejs.onError= function (err) {

    console.log(err.requireType);

    if (err.requireType === 'timeout') {

        console.log('modules: ' +err.requireModules);

    }

 

    throw err;

};

5加載器插件

RequireJS支持加載器插件。使用它們可以加載一些對於腳本正常工做很重要的非JS文件。RequireJS的wiki有一個插件的列表。本節討論一些由RequireJS一併維護的特定插件:

 

§ 5.1指定文本文件依賴

若是都能用HTML標籤而不是基於腳本操做DOM來構建HTML,是很不錯的。但沒有好的辦法在JavaScript文件中嵌入HTML。所能作的僅是在js中使用HTML字串,但這通常很難維護,特別是多行HTML的狀況下。

RequireJS有個text.js插件能夠幫助解決這個問題。若是一個依賴使用了text!前綴,它就會被自動加載。參見text.js的README文件。

 

§ 5.2頁面加載事件及DOM Ready

RequireJS加載模塊速度很快,頗有可能在頁面DOM Ready以前腳本已經加載完畢。須要與DOM交互的工做應等待DOM Ready。現代的瀏覽器經過DOMContentLoaded事件來知會。

可是,不是全部的瀏覽器都支持DOMContentLoaded。domReady模塊實現了一個跨瀏覽器的方法來斷定什麼時候DOM已經ready。下載並在你的項目中如此用它:

 

require(['domReady'],function (domReady) {

  domReady(function () {

    //一旦DOM準備就緒,本回調就執行。

    //在此函數中查詢及處理DOM是安全的。

  });

});

基於DOM Ready是個常規需求,像上述API中的嵌套調用方式,理想狀況下應避免。domReady模塊也實現了Loader PluginAPI,所以你可使用loader plugin語法(注意domReady依賴的!前綴)來強制require()回調函數在執行以前等待DOM Ready。當用做loader plugin時,domReady會返回當前的document:

 

require(['domReady!'],function (doc) {

    //本函數會在DOM ready時調用。

    //注意'domReady!'的值爲當前的document

});

注意:若是document須要一段時間來加載(也許是由於頁面較大,或加載了較大的js腳本阻塞了DOM計算),使用domReady做爲 loader plugin可能會致使RequireJS「超時」錯。若是這是個問題,則考慮增長waitSeconds配置項的值,或在require()使用 domReady()調用(將其當作是一個模塊)。

§ 5.3define I18N bundle

一旦你的web app達到必定的規模和流行度,提供本地化的接口和信息是十分有用的,但實現一個擴展良好的本地化方案又是很繁贅的。RequireJS容許你先僅配置一 個含有本地化信息的基本模塊,而不須要將全部的本地化信息都預先建立起來。後面能夠將這些本地化相關的變化以值對的形式慢慢加入到本地化文件中。

i18n.js插件提供i18n bundle支持。在模塊或依賴使用了i18n!前綴的形式(詳見下)時它會自動加載。下載該插件並將其放置於你app主JS文件的同目錄下。

將一個文件放置於一個名叫「nls」的目錄內來定義一個bundle——i18n插件當看到一個模塊名字含有「nls」時會認爲它是一個i18n bundle。名稱中的「nls」標記告訴i18n插件本地化目錄(它們應當是nls目錄的直接子目錄)的查找位置。若是你想要爲你的「my」模塊集提供 顏色名的bundle,應像下面這樣建立目錄結構:

 

my/nls/colors.js

該文件的內容應該是:

 

//my/nls/colors.js文件內容:

define({

    "root": {

        "red": "red",

        "blue": "blue",

        "green": "green"

    }

});

以一個含有「root」屬性的object直接量來定義該模塊。這就是爲往後啓用本地化所需的所有工做。你能夠在另外一個模塊中,如my/lamps.js中使用上述模塊:

 

//my/lamps.js內容

define(["i18n!my/nls/colors"],function(colors) {

    return {

        testMessage: "The name for red inthis locale is: " + colors.red

    }

});

my/lamps模塊具有一個「testMessage」屬性,它使用了colors.red來顯示紅色的本地化值。

往後,當你想要爲文件再增長一個特定的翻譯,如fr-fr,能夠改變my/nls/colors內容以下:

 

//my/nls/colors.js內容

define({

    "root": {

        "red": "red",

        "blue": "blue",

        "green": "green"

    },

    "fr-fr": true

});

而後再定義一個my/nls/fr-fr/colors.js文件,含有以下內容:

 

//my/nls/fr-fr/colors.js的內容

define({

    "red": "rouge",

    "blue": "bleu",

    "green": "vert"

});

RequireJS會使用瀏覽器的navigator.language或navigator.userLanguage屬性來斷定my/nls/colors的本地化值,所以你的app不須要更改。若是你想指定一個本地化方式,你可以使用模塊配置將該方式傳遞給插件:

 

requirejs.config({

    config: {

        //爲i18n作配置

        //module ID

        i18n: {

            locale: 'fr-fr'

        }

    }

});

注意 RequireJS老是使用小寫版本的locale值來避免大小寫問題,所以磁盤上i18n的全部目錄和文件都應使用小寫的本地化值。 RequireJS有足夠智能去選取合適的本地化bundle,使其儘可能接近my/nls/colors提供的那一個。例如,若是locale值時 「en-us」,則會使用「root」 bundle。若是locale值是「fr-fr-paris」,則會使用「fr-fr」 bundle。

RequireJS也會將bundle合理組合,例如,若french bundle以下定義(忽略red的值):

 

//my/nls/fr-fr/colors.js內容:

define({

    "blue": "bleu",

    "green": "vert"

});

則會應用「root」下的red值。全部的locale組件是如此。若是以下的全部bundle都已定義,則RequireJS會按照以下的優先級順序(最頂的最優先)應用值:

 

my/nls/fr-fr-paris/colors.js

my/nls/fr-fr/colors.js

my/nls/fr/colors.js

my/nls/colors.js

若是你不在模塊的頂層中包含root bundle,你可像一個常規的locale bundle那樣定義它。這種情形下頂層模塊應以下:

 

//my/nls/colors.js內容:

define({

    "root": true,

    "fr-fr": true,

    "fr-fr-paris": true

});

root bundle應看起來以下:

 

//my/nls/root/colors.js內容:

define({

    "red": "red",

    "blue": "blue",

    "green": "green"

});

 

http://blog.csdn.net/pigpigpig4587/article/details/23427573

相關文章
相關標籤/搜索