requireJs用一種新的script加載方法,這種方法和傳統<script>標籤是徹底不一樣的。它能夠運行地更快,而且進行更好地優化,它的主要目的就是爲了支持(encourage)模塊化(modular)代碼的加載。做爲其中的一部分,它支持利用模塊ID來加載script,而不是script標籤裏的url屬性。
javascript
requireJs加載的全部代碼地址都是相對於baseUrl的。頁面頂層script標籤有一個特殊的屬性data-main,require.js用它來啓動腳本加載頁面,而baseUrl一般設置成這個標籤所在的文件夾裏。data-main attribute是一個特殊的屬性,require.js會用這個屬性進行加載。下面這個例子會展現了baseUrl的設置:css
<!--This sets the baseUrl to the "scripts" directory, and loads a script that will have a module ID of 'main'--> <script data-main="scripts/main.js" src="scripts/require.js"></script>
或者,baseUrl能夠經過RequireJS config手動(manually)地設置。若是沒有明確的(explicit)config設置,或者沒有使用data-main屬性,那麼默認的baseUrl就是包含requireJs的HTML頁面所在的目錄。html
requireJs默認全部依賴(dependence)資源都死scripts,因此寫模塊ID時不須要.js的後綴。requireJs翻譯模塊ID的路徑時會自動加上.js尾綴的。運用paths config標籤,你能夠設置一組scripts腳本的位置。相對於<script>標籤,這些能讓你用更少的字符來加載script。java
有時候你想直接飲用一個script,而不是依照(conform)「baseUrl+paths"規則來找它。若是一個模塊ID由如下之一的規則,這個ID就不會經過」baseUrl+paths"配置來加載script,而是像普通的script url屬性來加載。jquery
一般地說,最好經過baseUrl和paths來設置模塊ID的路徑。這樣所,你能夠很方便地重命名和重定位腳本(configuring the paths to different locations)。git
類似的,爲了不配置凌亂,最好避免多級嵌套(deep folder hierarchies)的方式來加載代碼。要麼將全部的scripts放在baseUrl的目錄中,否則將你的代碼分置爲目錄(library)/第三方目錄庫(vendor)的結構,能夠像如下所示:github
in index.html:web
<script data-main="js/app.js" src="js/require.js"></script>
and in app.js:json
requirejs.config({ //設置默認模塊ID的路徑 js/lib baseUrl: 'js/lib', //另外,若是模塊ID以app開始, //它會從js/app目錄加載。paths設置時相對於baseUrl的,毫不會包括「.js」的,可是paths設置能夠是相對directory的 paths: { app: '../app' } });
開始main app的邏輯。
requirejs(['jquery', 'canvas', 'app/sub'], function ($, canvas, sub) { //jQuery, canvas and the app/sub module are all //loaded and can be used here now. });
在這個例子中,第三方庫(vendor實際上是供應商的意思)如jQuery,並無將它的版本號顯示在文件名中。若是你想跟蹤版本號,建議新開一個單獨的文件來記錄,或者你能夠用一些工具,像volo,能夠將package.json打上版本信息,但文件名仍是jQuery.js。這有助於你的配置最小化,避免爲每一個版本的庫設置paths路徑。例如,將"jquery"配置成(configure)「jquery-1,7,2"canvas
理想狀態下(ideally),每一個加載的腳本都是經過define()來定義的一個模塊。然而,有些"瀏覽器全局變量注入"型傳統/遺留(legacy)瀏覽器可能不能用define()來定義它們的依賴關係。爲此(for those),你能夠用shim config來解析它們的依賴關係。
若是你不想解析它們的依賴關係,你可能會獲得一些加載錯誤,基於速度的緣由(for speed),requireJs會異步( asynchronously)、無序(out of order)地加載腳本。
data-main屬性是一個特殊屬性,require.js在加載腳本的時候會檢查(check)它:
<!--當require.js加載的時候,它會忽視script/main.js的其餘script標籤屬性--> <script data-main="scripts/main" src="scripts/require.js"></script>
你能夠在data-main中設置配置選項,而後加載你的第一個應用模塊(application module)。注意:require.js的標籤加載的模塊是異步的async attribute。這意味着,若是你在這個頁面加載了其餘scripts,則不能保證經過require.js加載的頁面能夠先於這些腳本加載完畢。
舉個例子,如下的構造不能保證foo模塊的require.config的路徑設置會先於require()foo模塊執行:
<script data-main="scripts/main" src="scripts/require.js"></script> <script src="scripts/other.js"></script>
// contents of main.js: require.config({ paths: { foo: 'libs/foo-1.1.3' } });
// contents of other.js: //這段代碼可能會在main.js 的require.config()以前執行。若是這放生了,require.js會加載'scripts/foo.js‘額不是'scripts/libs/foo-1.1.3.js
require(['foo'], function(foo) { });
若是你想在HTML頁面中調用require(),最好不要用data-main。data-main只用在頁面只須要一個入口的時候。若是頁面想在行內調用require(),最好以下所示書寫代碼
<script src="scripts/require.js"></script> <script> require(['scripts/config']), function() { // Configuration loaded now, safe to do other require calls // that depend on that config. require(['foo'], function(foo) { }); }); </script>
一個模塊不一樣於傳統腳本文件的地方是,它定義了一個模塊範圍來避免污染全局環境。它明確地列舉了依賴的文件,並以函數(定義那個模塊的函數)參數的形式將這些依賴注入。RequireJs的模塊是Module Pattern的一個擴展,這樣的好處是不須要全局地引入其餘模塊。
RequireJs的模塊屬性讓它們能夠儘快地被加載,就算加載是無序的,依賴也會按照爭取的順序。由於沒有建立全局變量,因此在一個頁面中能夠建立多個版本的模塊load multiple versions of a module in a page.
(若是你熟悉或者使用過commonjs,那麼請看 CommonJS Notes來了解requireJs和CommonJs的映射(map to)關係)
一個磁盤文件應該只定義一個模塊。optimization tool工具能夠將模塊分組優化(grouped into optimized bundles).
若是一個模塊沒有任何依賴,僅含任何值/對(name/value),則在define()中定義這些值/對就行了。
//Inside file my/shirt.js: define({ color: "black", size: "unisize" });
若是以個模塊沒有依賴,可是須要一些函數來作一些setup的工做,那就在define中定義該函數:
//my/shirt.js now does setup work //before returning its module definition. define(function () { //Do setup work here return { color: "black", size: "unisize" } });
若是一個模塊有依賴,第一個參數(arguments)應該是一串依賴名的數組,第二個參數應該是定義的函數。一旦全部的依賴加載完畢,這個函數就會被調用來定義該模塊。定義這個模塊的函數應該返回一個對象。依賴會以一個參數的形式傳給函數,參數列表和依賴名稱列表一一對應
//my/shirt.js 有一些依賴, cart和inventory,都和shirt.js在同一個目錄下 define(["./cart", "./inventory"], function(cart, inventory) { //return an object to define the "my/shirt" module. return { color: "blue", size: "large", addToCart: function() { inventory.decrement(this); cart.add(this); } } } );
這個函數有兩個參數「cart」和「inventory」。對應的模塊以模塊名"./cart" 和"./inventory"展現。
這個函數會在my/cart ,my/inventory被加載後調用,而且得到cart和inventory的函數參數。
嚴重不容許模塊定義全局的變量,這樣,多版本的模塊才能存在於同一個頁面。另外,函數的參數順序應該和依賴模塊的順序同樣。
返回的對象定義了"my/shirt"模塊。這樣定義模塊,"my/shirt"就不會以全局對象的方式存在。
模塊的並不必定要有返回值。任何函數的返回值(return value)都是容許的。此處有一個模塊如它所定義地返回了一個函數:
/A module definition inside foo/title.js. It uses //my/cart and my/inventory modules from before, //but since foo/title.js is in a different directory than //the "my" modules, it uses the "my" in the module dependency //name to find them. The "my" part of the name can be mapped //to any directory, but by default, it is assumed to be a //sibling to the "foo" directory. define(["my/cart", "my/inventory"], function(cart, inventory) { //return a function to define "foo/title". //It gets or sets the window title. return function(title) { return title ? (window.title = title) : inventory.storeName + ' ' + cart.name; } } );
若是你想從新用一些以CommonJS module format的方式寫的代碼,而這些代碼難以用上述的依賴數組的方式來寫,你能夠考慮直接將這些依賴對應到本地變量。你可使用一個簡單的commonJs包裝來實現simplified CommonJS wrapper:
define(function(require, exports, module) { var a = require('a'), b = require('b'); //Return the module value return function () {}; } );
這個包裝依賴於Function.prototype.toString()將函數內容賦予有意義的字符串。這PS3或者一些老的Opera手機瀏覽器上表現很差。用optimizer將這些依賴變成一個數組的格式,以便與在這些設備上用。
更多的信息參考 CommonJS page, "Sugar" section in the Why AMD page.
你可能會看到一個define()中包含了模塊名做爲第一個參數:
//Explicitly defines the "foo/title" module: define("foo/title", ["my/cart", "my/inventory"], function(cart, inventory) { //Define foo/title object in here. } );
這些一般是optimization tool生成的。你能夠本身命名模塊名,但這會使模塊更難移植——若是你將文件夾移到另外一個目錄中,你還得改它們的名字。最好避免對模塊硬編碼,而讓optimization tool來生成模塊名。這個工具須要生成模塊名,以便將這些模塊打成一個包(be bundled),使瀏覽器更快加載。
One module per file.: 每一個js文件只能定義一個模塊,這是模塊尋找機制的天然要求。多個模塊會被 optimization tool打包成一個模塊,但你須要用將多個模塊放到一個文件夾裏.
Relative module names inside define(): 對於define()中("./relative/name")的模塊調用,確保將require自己做爲一個依賴注入到模塊中, 這樣,相對路徑的名稱才能被正確地解析:
define(["require", "./relative/name"], function(require) { var mod = require("./relative/name"); });
更好的方式是,像commomjs同樣使用一個更短的解析:
define(function(require) { var mod = require("./relative/name"); });
這個模塊利用Function.prototype.toString()去尋找require()的調用,並把它們加到依賴的數組中。在require()的工做路徑下,代碼會被正確的解析。
若是你在一個目錄中建立了不少模塊,相對路徑一般是頗有用的。你分享這個目錄給其餘人,你也能夠對這個目錄中的其餘模塊作一些改動,而沒必要知道模塊的名稱。
Generate URLs relative to module: 你可能須要生成一個相對於模塊的URL. 你能夠將require做爲一個依賴注入,而後用require.toUrl()來生成一個url:
define(["require"], function(require) { var cssUrl = require.toUrl("./style.css"); });
Console debugging: 若是你想調用一個已經經過 require(["module/name"] , function(){}) 方式加載的模塊,你能夠用字符串做爲模塊名的參數來調用它:
require("module/name").callSomeFunction()
注意,這個只對於已經用"module/name"方式異步加載的模塊有效。若是用一個相對路徑,像'./module/name',那隻會在define內部工做有效。
若是你定義了一個循環依賴("a" needs "b" and "b" needs "a"), 那麼當"b"模塊被調用的時候, 它會獲得一個未定義的a值。「b」能夠在已經被require()定義好後來獲取「a」(確保將require做爲一個依賴注入):
//Inside b.js: define(["require", "a"], function(require, a) { //"a" in this case will be null if "a" also asked for "b", //a circular dependency. return function(title) { return require("a").doSomething(); } } );
一般,你不須要用require()來獲取模塊, 只要將依賴的模塊做爲參數傳給函數就能夠了.循環依賴是不多出現的,若是出現了,你就想從新考慮設計問題了。然而,有時候也是須要循環依賴的,若是這樣的話,能夠考慮上述的代碼組織。
若是你很是熟悉commonJs,你也能夠用exports的來製造一個空模塊,這也能夠以依賴的形式被其餘模塊獲取。這樣作的話,你能夠在循環依賴的任意一方安全地使用其餘模塊。.這隻在每一個模塊都以object做爲模塊值輸出的時候有效,函數形式無效:
//Inside b.js: define(function(require, exports, module) { //If "a" has used exports, then we have a real //object reference here. However, we cannot use //any of "a"'s properties until after "b" returns a value. var a = require("a"); exports.foo = function () { return a.bar(); }; });
或者,若是你再用數組做爲依賴,能夠調用特殊的exports模塊 'exports' dependency:
//Inside b.js: define(['a', 'exports'], function(a, exports) { //If "a" has used exports, then we have a real //object reference here. However, we cannot use //any of "a"'s properties until after "b" returns a value. exports.foo = function () { return a.bar(); }; });
JSONP 是javascript中服務調用的一種方式。它經過script標籤發起HTTP GET請求,是實現跨域的一種公認手段。
要在requireJs使用JSONP,要在回調中將參數屬性設置爲」define「。這意味着你能夠將獲取到的JSONP URL的值做爲一個模塊定義。
這裏有一個調用JSONP API端點的例子。在這個例子中,JSONP callback參數的值設置爲」callback「, 因此"callback=define"告訴API將返回的JSON打包放在define()中:
require(["http://example.com/api/data.json?callback=define"], function (data) { //The data object will be the API response for the //JSONP data call. console.log(data); } );
這種JSONP的用法僅限於JSONP服務的初始化initial application setup. 一旦JSONP服務超時,意味着其餘經過define()的模塊不會執行,因此這種錯誤處理是不強健的。
Only JSONP return values that are JSON objects are supported.一個JSONP的返回是一個數組,若是是一個字符串或者數字式不會工做的。
這個機制不能被用在long-polling 類的JSONP鏈接-- 那些用來處理實時流的APIs. 這種類型的api在收到每一個返回的時候通常會作script清理,RequireJS只會獲取一次JSONP URL—— 後繼使用require()或者define()發起的URL會獲得一個緩存值。
加載JSONP服務的錯誤一般以服務超時的形式出現。由於簡單加載一個script標籤不會獲得不少網絡錯誤信息。你能夠用override requirejs.onError() 來獲取錯誤。更多信息參見 Handling Errors section.
有一個全局的函數requirejs.undef(), 用來undefining一個模塊.它會重設加載器的內部(internal)狀態來消除前一個模塊的定義。
然而,若是一個模塊已經被定義了,而且在其餘模塊中做爲一個依賴被調用執行了,全局函數是不會清除這個模塊的。 因此它僅在無其餘模塊持有該模塊錯誤的時候有用,或者當將來須要加載這模塊時有點用。 See the errback section for an example.
若是想知道更多關於undefined的複雜語法,the semi-private onResourceLoad API may be helpful.