RequireJS 模塊的定義與加載


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

RequireJS的模塊語法允許它盡快地加載多個模塊,雖然加載的順序不定,但依賴的順序最終是正確的。同時因為無需創建全局變量,甚至可以做到在同一個頁面上同時加載同一模塊的不同版本。

(如果你熟悉ConmmonJS,可參看CommonJS的注釋信息以了解RequireJS模塊到CommonJS模塊的映射關系)。
一個磁盤文件應該只定義 1 個模塊。多個模塊可以使用內置優化工具將其組織打包。

模塊的定義

簡單的鍵值對

如果一個模塊僅含值對,沒有任何依賴,則在define()中定義這些值對就好了:

define({ name: "john", age : 20, sex : "femal" }); 

點評:這種情況用途不是很大,一般用於簡單的數據模擬。加載該模塊后,回調函數中注入的參數是該鍵值對Object對象,即:

 { name: "john", age : 20, sex : "femal" } 

函數式定義

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

define(function () {
    //Do setup work here return { color: "black", size: "unisize" } }); 

點評:這種適合無依賴模塊的編寫,且回調函數中注入的參數是根據函數中return的返回值確定的,如果沒有return語句,則默認返回undefined

存在依賴的函數式定義

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

//my/shirt.js now has some dependencies, a cart and inventory //module in the same directory as 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); } } } ); 

本示例創建了一個my/shirt模塊,它依賴於my/cart及my/inventory。磁盤上各文件分布如下:

  • my/cart.js
  • my/inventory.js
  • my/shirt.js

模塊函數以參數"cart"及"inventory"使用這兩個以"./cart"及"./inventory"名稱指定的模塊。在這兩個模塊加載完畢之前,模塊函數不會被調用

嚴重不鼓勵模塊定義全局變量。遵循此處的定義模式,可以使得同一模塊的不同版本並存於同一個頁面上(參見 高級用法 )。另外,函參的順序應與依賴順序保存一致

返回的object定義了"my/shirt"模塊。這種定義模式下,"my/shirt"不作為一個全局變量而存在。

點評:這算是模塊中最常見的用法了。注意上面強調的部分。

將模塊定義為一個函數

對模塊的返回值類型並沒有強制為一定是個object,任何函數的返回值都是允許的。此處是一個返回了函數的模塊定義:

//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來定義模塊

如果你現有一些以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手機瀏覽器中不起作用。考慮在這些設備上使用優化器將依賴導出為數組形式。

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

點評:這種用法跟上面說的一樣,首先回調用Function.prototype.toString()將函數轉換成字符串,然后在進行詞法分析,這樣依賴,就比較低耗了,原則上不考慮使用這種方法進行模塊封裝。

定義一個命名模塊

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

    //Explicitly defines the "foo/title" module: define("foo/title", ["my/cart", "my/inventory"], function(cart, inventory) { //Define foo/title object in here. } ); 

這些常由優化工具生成。你也可以自己顯式指定模塊名稱,但這使模塊更不具備移植性——就是說若你將文件移動到其他目錄下,你就得重命名。一般最好避免對模塊硬編碼,而是交給優化工具去生成。優化工具需要生成模塊名以將多個模塊打成一個包,加快到瀏覽器的載人速度。

點評:define 函數有三個參數,第一個 id 即模塊名稱,這個名稱的格式是相對於 baseUrl 的路徑除去文件格式,比如 baseUrl 為 js 目錄,一個模塊放在 js/libs/hi.js 里,則如果名稱是這樣定義的:

define('libs/hi', ['jquery'], function($){......}); 

這樣的定義形式的好處是,模塊不可能沖突,因為同一目錄下不允許同名文件。但也因此 require.js 建議我們不要設置模塊名稱,因為設置了 ‘libs/hi’ 的模塊名稱后,模塊就必須放在 js/libs 目錄下的 hi.js 文件中,要移動位置的話,模塊名稱要跟着改變。至於后期利用 r.js 優化時生成了模塊名稱,那已經是另外一回事。所以定義模塊時,第一參數常常會忽略。

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) { //The data object will be the API response for the //JSONP data call. console.log(data); } ); 

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

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

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

JSONP調用錯誤一般以服務超時的形式出現,因為簡單加載一個script標簽一般不會得到很 詳細的網絡錯誤信息。你可以override requirejs.onError()來過去錯誤。更多的信息請參看錯誤處理部分。

點評:跨域腳本調用。

模塊的加載

常見模塊加載調用的形式如下:

require(["jquery"],function($){ //todo }); 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM