轉: requirejs中文api (詳細)

 

RequireJS的目標是鼓勵代碼的模塊化,它使用了不一樣於傳統<script>標籤的腳本加載步驟。能夠用它來加速、優化代碼,但其主要目的仍是爲了代碼的模塊化。它鼓勵在使用腳本時以module ID替代URL地址。~~~模塊化的代碼,更清晰和易於維護

§ 1.1加載JavaScript文件
RequireJS以一個相對於baseUrl的地址來加載全部的代碼。
<script data-main="js/main" src="scripts/require.js"></script>
若加載require.js的<script>標籤 包含data-main屬性,則baseUrl爲main.js所在的目錄(在這裏就是 js目錄)

<script src="scripts/require.js"></script>
baseUrl亦可經過requirejs.config手動設置。若是沒有顯式指定config及data-main,則默認的baseUrl爲包含require.js的那個HTML頁面的所屬目錄。

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

~~~注: requirejs == require // true
~~~ 開發時,讓firefox禁用緩存,about:config network.http.use-cache = false 這樣是不可取的(開發完會忘記設置回true, 並且實踐發現firebug的網絡面板請求列表仍是不會刷新),因此應該在 firebug網絡面板下拉小三角中選擇 禁用瀏覽器緩存

示例:
假設目錄結構爲:
www/
index.html
js/
app/
  sub.js
lib/
  jquery.js
  canvas.js
test/
  mytest.js
app.js

index.html:
<script data-main="js/app.js" src="js/require.js"></script> ~~~baseUrl這裏默認爲 js目錄

app.js:
requirejs.config({

// 默認從js/lib加載全部的module ID
baseUrl: 'js/lib',

// 除非,module ID以"app"開頭,則從js/app目錄加載。注意,paths config是相對於baseUrl的,
//並且不要包含".js"的後綴,由於一個path有多是個目錄
paths: {
app: '../app',
test: '../test/mytest'
//~~~paths對象字面量中設置的路徑能夠是文件夾,也能夠是具體的文件,注意不能添加.js後綴,如:test: '../test/mytest.js', 那麼請求的將會是 /js/test/mytest.js.js
}
});

~~~ cdn fallback的例子。當cdn的jquery不可用時,用本地的jquery
requirejs.config({
baseUrl: 'js',
paths:{ // cdn fallback
jquery:[
'//cdnjs.cloudflare.com/ajax/libs/jquery/2.0.0/jquery.min999',
'../../seatest/jquery'
]
}
});


// 啓動main app
requirejs(['jquery', 'canvas', 'app/sub', 'test'], function ($, canvas, sub, test) {
//自此,jQuery,canvas以及app/sub模塊
//都已加載並可開始使用了。
});

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

以」.js」結尾;
以」/」開頭;
包含URL協議,如」http:」、」https:」
~~~moduleID帶.js後綴或絕對路徑時,不採用 baseUrl + paths的解析方式,也不會自動爲moduleID添加.js後綴,而是根據相對路徑或絕對路徑的方式去解析,因此要帶.js後綴;如果baseUrl + paths的路徑解析方式,則會自動添加.js後綴。如:

~~~baseUrl + paths方式去解析moduleID
requirejs(['app/sub'], function(sub){
// some code goes here
});

~~~相對路徑方式去解析 app/sub.js 相對路徑是相對包含requirejs的html頁面的
requirejs(['app/sub.js'], function(sub){
// some code goes here
});

~~~絕對路徑方式去解析
requirejs(['http://localhost/js/app/sub.js'], function(sub){
// some code goes here
});

~~~絕對路徑方式去解析
requirejs(['/js/app/sub.js'], function(sub){
// some code goes here
});

 

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

§ 1.2定義模塊

模塊不一樣於傳統的腳本文件,它良好地定義了一個做用域來避免全局名稱空間污染。它能夠顯式地列出其依賴關係,並以函數(定義此模塊的那個函數)參數的形式將這些依賴進行注入,而無需引用全局變量。RequireJS的模塊是模塊模式的一個擴展,其好處是無需全局地引用其餘模塊。

RequireJS的模塊語法容許它儘快地加載多個模塊,雖然加載的順序不定,但依賴的順序最終是正確的。同時由於無需建立全局變量,甚至能夠作到在同一個頁面上同時加載同一模塊的不一樣版本。

(若是你熟悉ConmmonJS,可參看CommonJS的註釋信息以瞭解RequireJS模塊到CommonJS模塊的映射關係)。

一個磁盤文件應該只定義 1 個模塊。多個模塊可使用內置優化工具將其組織打包。

§ 1.2.1 簡單的值對
若是一個模塊僅含值對,沒有任何依賴,則在define()中定義這些值對就行了:
//my/shirt.js:
define({color: "black", size: "unisize"});

§ 1.2.2 函數式定義
若是一個模塊沒有任何依賴,但須要一個作setup工做的函數,則在define()中定義該函數,並將其傳給define():
//my/shirt.js 如今在返回其模塊定義以前作了一些setup工做
define(function () {
//Do setup work here
return {
color: "black",
size: "unisize"
}
});

§ 1.2.3 存在依賴的函數式定義

若是模塊存在依賴:則第一個參數是依賴的名稱數組;第二個參數是函數,在模塊的全部依賴加載完畢後,該函數會被調用來定義該模塊,所以該模塊應該返回一個定義了本模塊的object。依賴關係會以參數的形式注入到該函數上,參數列表與依賴名稱列表一一對應。
~~~ define( depsArray, callback ) 注意回調函數會在全部依賴模塊都加載完後才執行
//my/shirt.js 如今對同目錄下的cart及inventory存在依賴
define(["./cart", "./inventory"], function(cart, inventory) {
//返回一個定義了該"my/shirt"模塊的object
return {
color: "blue",
size: "large",
addToCart: function() {
inventory.decrement(this);
cart.add(this);
}
}
});

§ 1.2.4 將模塊定義爲一個函數
~~~相似nodejs 模塊返回值會被賦值給module.exports 因此模塊返回值能夠是任何數據類型,不必定是對象字面量
對模塊的返回值類型並無強制爲必定是個object,任何函數的返回值都是容許的。此處是一個返回了函數的模塊定義:

//foo/title.js 像以前同樣使用了my/cart及my/inventory模塊,但foo/bar.js位於不一樣於"my"模塊的目錄下,它在模塊依賴名稱中使用"my"來定位它們。依賴名稱中的"my"可能映射到任意一個目錄,但默認地,假定它鄰接着"foo"目錄。
假設目錄結構爲:
js/
foo/
  title.js
my/
  cart.js
  inventory.js
  main.js

main.js:
requirejs.config({
paths:{
my: 'js/my'
}
});

title.js:
define(["my/cart", "my/inventory"], function(cart, inventory) {
//返回一個函數以定義"foo/title". 它獲取/設置window的title
return function(title) {
return title ? (window.title = title) : inventory.storeName + ' ' + cart.name;
}
});

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

define(function(require, exports, module) {
var a = require('a'),
b = require('b');
//Return the module value
return function () {};
});
~~~正則匹配函數體內的require(),提取出依賴數組
該包裝方法依靠Function.prototype.toString()將函數內容賦予一個有意義的字串值,但在一些設備如PS3及一些老的Opera手機瀏覽器中不起做用。考慮在這些設備上使用optimizer將依賴導出爲數組形式。

更多的信息可參看CommonJS Notes頁面,以及」Why AMD」頁面的」Sugar」段落。


§ 1.2.6 定義一個命名模塊 ~~不推薦的作法

你可能會看到一些define()中包含了一個模塊名稱做爲首個參數:
//顯式地定義"foo/title"模塊:
define("foo/title", ["my/cart", "my/inventory"], function(cart, inventory) {
//此處定義foo/title object
});
這些常由優化工具生成。你也能夠本身顯式指定模塊名稱,但這使模塊更不具有移植性——就是說若你將文件移動到其餘目錄下,你就得重命名。通常最好避免對模塊硬編碼,而是交給優化工具去生成。優化工具須要生成模塊名以將多個模塊打成一個包,加快到瀏覽器的載人速度。

§ 1.2.7 其餘注意事項

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

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()調用來獲取它:

注意這種形式僅在"module/name"已經由其異步形式的require(["module/name"])加載了後纔有效。只能在define內部使用形如"./module/name"的相對路徑。
define(['require', './time2'], function(){
var time2 = require('./time2');
time2.show();
})

§ 1.2.8 循環依賴

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

//b.js:
define(["require", "a"],
function(require, a) {
//"a"將是null,若是a/b間是循環依賴
return function(title) {
return require("a").doSomething();
}
}
);

示例:
//定義存在循環依賴的a b模塊
//module a:
define(['b'], function(b){
return {
color:function(){return 'blue';},
des:function(){ console.log( b() ); }
}
});

//定義存在循環依賴的a b模塊
//module b:
define(['require', 'a'],function(require, a){
return function(){
// debugger; ~~~當調用該函數時, a.js b.js都已加載 require('a')得到其外部接口
var color = require('a').color();
console.log(require('a'));// {color:.., des:..}
return 'sky is ' + color;
}
});


通常說來你無需使用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();
};
});


> 用commonJS的模塊定義方式,解決模塊的循環依賴問題
//定義存在循環依賴的a b模塊
//module a:
define(function(require, exports, module){
var b2 = require('b2');
exports.color = function(){return 'blue';};
exports.des = function(){ console.log( b2.say() ); }
});

//定義存在循環依賴的a b模塊
//module b:
define(function(require, exports, module){
var a2 = require('a2');
exports.say = function(){ return 'commonJS : sky is ' + a2.color(); }
debugger; //在這裏b2模塊已返回值 require('a2') == {}, require('b2') == { say: ...}
});

> 若改成這樣的方式 則會報錯
//定義存在循環依賴的a b模塊
//module a:
define(function(require, exports, module){
var b2 = require('b2');
exports = { //~~exports 和 module.exports指向同一對象,重寫exports後,關聯丟失 因此是不行的
color: function(){return 'blue';};
des: function(){ console.log( b2.say() ); }
}
});

//定義存在循環依賴的a b模塊
//module b:
define(function(require, exports, module){
var a2 = require('a2');
exports = {
say : function(){ return 'commonJS : sky is ' + a2.color(); }
debugger;
}
});

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

//b.js:
define(['a', 'exports'], function(a, exports) {
//若"a"使用了exports,則此處咱們就擁有了一個真正的object引用。
//但在b返回值以前咱們沒法使用a的任何屬性。

exports.foo = function () {
return a.bar();
};
});
~~~導入內置的exports模塊,其實和commonJS模塊定義方式同樣原理


§ 1.2.9 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);
}
);
~~~模塊的定義來自對不一樣域的遠程請求

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

僅支持返回值類型爲JSON object的JSONP服務,其餘返回類型如數組、字串、數字等都不能支持。

這種功能不應用於long-polling類的JSONP鏈接——那些用來處理實時流的API。這些API在接收響應後通常會作script的清 理,而RequireJS則只能獲取該JSONP URL一次——後繼使用require()或define()發起的的對同一URL的依賴(請求)只會獲得一個緩存過的值。

JSONP調用錯誤通常以服務超時的形式出現,由於簡單加載一個script標籤通常不會獲得很 詳細的網絡錯誤信息。你能夠override requirejs.onError()來獲取錯誤。更多的信息請參看錯誤處理部分。

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

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

若是你打算在undefine時作一些複雜的依賴圖分析,則半私有的onResourceLoad API可能對你有用。

§ 2 機理
RequireJS使用head.appendChild()將每個依賴加載爲一個script標籤。

RequireJS等待全部的依賴加載完畢,計算出模塊定義函數正確調用順序,而後依次調用它們。

在同步加載的服務端JavaScript環境中,可簡單地重定義require.load()來使用RequireJS。build系統就是這麼作的。該環境中的require.load實現可在build/jslib/requirePatch.js中找到。

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

§ 3 配置項
當在頂層HTML頁面(或不做爲一個模塊定義的頂層腳本文件)中,可將配置做爲首項放入:
<script src="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>
<script src="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()來聲明依賴關係、設置模塊的"瀏覽器全局變量注入"型腳本作依賴和導出配置。

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

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所使用。

那些僅做爲jQuery或Backbone的插件存在而不導出任何模塊變量的"模塊"們,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」。

§ 4 進階應用
§ 4.1從包中加載模塊

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標籤:

<script data-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」約定。
§ 4.2多版本支持

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

<script src="../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 with version: " +
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 with version: " +
omega.version); //prints 2
}
);
}, 100);
});
</script>

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

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

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

你可能須要設置baseUrl配置項來確保require()可找到待加載腳本。
你能夠在unit test使用的一個文件中找到一個例子。
§ 4.5對Rhino的支持

RequireJS可經過r.js適配器用在Rhino中。參見r.js的README。
§ 4.6處理錯誤

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

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

requireType:含有類別信息的字串值,如「timeout」,「nodefine」, 「scripterror」
requireModules: 超時的模塊名/URL數組。

若是你獲得了requireModules錯,可能意味着依賴於requireModules數組中的模塊的其餘模塊未定義。
§ 4.6.1 在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()調用起到相同的目的。
§ 4.6.2 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()僅用於聲明模塊。

§ 4.6.3 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查找解析,由於那在瀏覽器中是低效的。

§ 4.6.4 全局的 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 Plugin API,所以你可使用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 in this 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"
});

 javascript

requirejs的優勢:
JS代碼模塊化(代碼職責更清晰,各模塊都在本身的做用域下執行,不污染全局環境),
加速JS文件加載(無序非阻塞方式加載腳本,按依賴順序執行), 
自動依賴管理(模塊的依賴未加載完以前,不執行模塊內的JS)

requirejs api:
define: 全局函數,用於定義模塊,每一個模塊都有一個惟一的模塊ID,用於requirejs的運行時函數。
require: 全局函數,用於讀取依賴。通常用於入口模塊,定義入口模塊和入口模塊的依賴。
config: 用於配置requirejs, 在requirejs命名空間下。

data-main: 指定baseUrl
< script src="scripts/require.js" data-main="scripts/app.js">< /script>

config:

require.config({
    //By default load any module IDs from scripts/app
    baseUrl: 'scripts/app',
    //except, if the module ID starts with "lib"
     paths: {
        lib: '../lib'
    },
    // load backbone as a shim
    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'
        }
    }
});

模塊定義:
define函數接受一個依賴數組和一個包含模塊定義的函數。

define(["logger"], function(logger) {       
        return {
             firstName: 「John",
             lastName: 「Black「,
             sayHello: function () {
                logger.log(‘hello’);
             }
        }
    }
);

每個模塊都應該返回它的API.這個示例中咱們有兩個屬性(firstName和lastName)和一個函數(sayHello)。而後,只要你後面定義的模塊經過ID來引用這個模塊,你就可使用其暴露的API。

require函數
在RequireJS中另一個很是有用的函數是require函數。require函數用於加載模塊依賴但並不會建立一個模塊。例如:下面就是使用require定義了可以使用jQuery的一個函數。

require(['jquery'], function ($) {
    //jQuery was loaded and can be used now
});
相關文章
相關標籤/搜索