RequireJS進階:模塊的定義與加載

概述

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

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

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

模塊的定義

簡單的鍵值對

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

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

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

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

函數式定義

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

define(function () {
    //Do setup work here

    return {
        color: "black",
        size: "unisize"
    }
});

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

存在依賴的函數式定義

若是模塊存在依賴:則第一個參數是依賴的名稱數組;第二個參數是函數,在模塊的全部依賴加載完畢後,該函數會被調用來定義該模塊,所以該模塊應該返回一個定義了本模塊的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

});

第一個參數指定要加載的模塊名,第二個是相對應的注入參數。

幫助文檔

官網API文檔:http://requirejs.org/docs/api.html#define
中文API文檔:http://requirejs.cn/docs/api.html#define
中文API文檔:http://makingmobile.org/docs/tools/requirejs-api-zh/#define
中文文檔:http://www.zfanw.com/blog/require-js.html
中文文檔:http://www.fanli7.net/a/bianchengyuyan/ASP/20130419/300243.html

相關文章
相關標籤/搜索