1. 程式人生 > >服從AMD規範的require.js庫的學習

服從AMD規範的require.js庫的學習

服從AMD規範的require.js庫的學習

要學習該庫,要懂得點規範的知識。在服務端程式設計,我們都知道Java、C++等引入別的模組就可以使用其功能,程式執行的速度也很快;我們在編寫前端程式碼會使用到js語言,這個是沒有模組概念的,我們必須在HTML文字中寫上一大段<script>\標籤來引用還要注意引用的檔案的順序,因為檔案之間的依賴關係,此情景就會造成網頁響應的延時。所以大救星來了——require.js庫,該庫以服從AMD規範,即非同步模組定義。

AMD規範:全稱是Asynchronous Module Definition,即非同步模組載入機制。從它的規範描述頁面看,AMD很短也很簡單,但它卻完整描述了模組的定義,依賴關係,引用關係以及載入機制。從它被requireJS,NodeJs,Dojo,JQuery使用也可以看出它具有很大的價值,沒錯,JQuery近期也採用了AMD規範。在這篇文章中,我們就將介紹AMD的性質,用法,優勢以及應用場景。從AMD中我們也能學習到如何在更高層面去設計自己的前端應用。

作為一個規範,只需定義其語法API,而不關心其實現。AMD規範簡單到只有一個API,即define函式:

define([module-name?], [array-of-dependencies?], [module-factory-or-object]);

其中:
module-name: 模組標識,可以省略。
array-of-dependencies: 所依賴的模組,可以省略。
module-factory-or-object: 模組的實現,或者一個JavaScript物件。
從中可以看到,第一個引數和第二個引數都是可以省略的,第三個引數則是模組的具體實現本身。


如何定義一個模組

  1. AMD例項

下面程式碼定義了一個alpha模組,並且依賴於內建的require,exports模組,以及外部的beta模組。可以看到,第三個引數是回撥函式,可以直接使用依賴的模組,他們按依賴宣告順序作為引數提供給回撥函式。
這裡的require函式讓你能夠隨時去依賴一個模組,即取得模組的引用,從而即使模組沒有作為引數定義,也能夠被使用;exports是定義的alpha 模組的實體,在其上定義的任何屬性和方法也就是alpha模組的屬性和方法。通過exports.verb = …就是為alpha模組定義了一個verb方法。例子中是簡單呼叫了模組beta的verb方法。

define("alpha", ["require", "exports", "beta"], function (require, exports, beta) {
exports.verb = function() {
return beta.verb();
//or:
return require("beta").verb();
}
});
  1. 匿名模組

define 方法允許你省略第一個引數,這樣就定義了一個匿名模組,這時候模組檔案的檔名就是模組標識。如果這個模組檔案放在a.js中,那麼a就是模組名。可以在依賴項中用"a"來依賴於這個匿名模組。這帶來一個好處,就是模組是高度可重用的。你拿來一個匿名模組,隨便放在一個位置就可以使用它,模組名就是它的檔案路徑。這也很好的符合了DRY(Don’t Repeat Yourself)原則。
下面的程式碼就定義了一個依賴於alpha模組的匿名模組:

define(["alpha"], function (alpha) {
return {
verb: function(){
return alpha.verb() + 2;
}
};
});
  1. define

僅有一個引數的define
前面提到,define的前兩個引數都是可以省略的。第三個引數有兩種情況,一種是一個JavaScript物件,另一種是一個函式。
如果是一個物件,那麼它可能是一個包含方法具有功能的一個物件;也有可能是僅提供資料。後者和JSON-P非常類似,因此AMD也可以認為包含了一個完整的 JSON-P實現。模組演變為一個簡單的資料物件,這樣的資料物件是高度可用的,而且因為是靜態物件,它也是CDN友好的,可以提高JSON-P的效能。考慮一個提供中國省市對應關係的JavaScript物件,如果以傳統JSON-P的形式提供給客戶端,它必須提供一個callback函式名,根據這個函式名動態生成返回資料,這使得標準JSON-P資料一定不是CDN友好的。但如果用AMD,這個資料檔案就是如下的形式:

define({
provinces: [
{
name: '上海名',
areas: ['浦東新區', '徐彙區']},
{
name: '江蘇',
cities: ['南京', '南通']}
//.....
]
});

假設這個檔名為china.js,那麼如果某個模組需要這個資料,只需要:

define(['china'], function(china){
//在這裡使用中國省市資料
});

通過這種方式,這個模組是真正高度可複用的,無論是用遠端的,還是Copy到本地專案,都節約了開發時間和維護時間。
如果引數是一個函式,其用途之一是快速開發實現。適用於較小型的應用,你無需提前關注自己需要什麼模組,自己給誰用。在函式中,可以隨時require自己需要的模組。例如:

define(function(){
var p = require('china');
//使用china這個模組
});

即你省略了模組名,以及自己需要依賴的模組。這不意味著你無需依賴於其他模組,而是可以讓你在需要的時候去require這些模組。define方法在執行的時候,會呼叫函式的toString方法,並掃描其中的require呼叫,提前幫助你載入這些模組,載入完成之後再執行。這使得快速開發成為可能。需要注意的一點是,Opera不能很好的支援函式的toString方法,因此,在瀏覽器中它的適用性並不是很強。但如果你是通過build工具打包所有的 JavaScript檔案,這將不是問題,構建工具會幫助你掃描require並強制載入依賴的模組。