Javascript的AMD規範

AMD規範的全稱是Asynchronous Module Definition,即異步模塊加載機制。從它的規範描述頁面看,AMD很短也很簡單,但它卻完整描述了模塊的定義,依賴關係,引用關係以及加載機制。從它被requireJS,NodeJs,Dojo,JQuery使用也可以看出它具有很大的價值,沒錯,JQuery近期也採用了AMD規範。

1.簡介

作爲一個規範,只需定義其語法API,而不關心其實現。AMD規範簡單到只有一個API,即define函數:
define([module-name?], [array-of-dependencies?], [module-factory-or-object]);
其中:module-name: 模塊標識,可以省略。
array-of-dependencies: 所依賴的模塊,可以省略。
module-factory-or-object: 模塊的實現,或者一個JavaScript對象。
從中可以看到,第一個參數和第二個參數都是可以省略的,第三個參數則是模塊的具體實現本身。後面將介紹在不同的應用場景下,他們會使用不同的參數組合。
從這個define函數AMD中的A:Asynchronous,我們也不難想到define函數具有的另外一個性質,異步性。當define函數執行時,它首先會異步的去調用第二個參數中列出的依賴模塊,當所有的模塊被載入完成之後,如果第三個參數是一個回調函數則執行,然後告訴系統模塊可用,也就通知了依賴於自己的模塊自己已經可用。如果對應到dojo1.6之前的實現,那麼在功能上可以有如下對應關係:
module-name: dojo.provide
dependencies: dojo.require
module-factory: dojo.declare
不同的是,在加載依賴項時,AMD用的是異步,而dojo.require是同步。異步和同步的區別顯而易見,前者不會阻塞瀏覽器,有更好的性能和靈活性。而對於NodeJs這樣的服務器端AMD,則模塊載入無需阻塞服務器進程,同樣提高了性能。

2.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();
//或者:
return require("beta").verb();
}

3.匿名模塊編輯

define 方法允許你省略第一個參數,這樣就定義了一個匿名模塊,這時候模塊文件的文件名就是模塊標識。如果這個模塊文件放在a.js中,那麼a就是模塊名。可以在依賴項中用"a"來依賴於這個匿名模塊。這帶來一個好處,就是模塊是高度可重用的。你拿來一個匿名模塊,隨便放在一個位置就可以使用它,模塊名就是它的文件路徑。這也很好的符合了DRY(Don't Repeat Yourself)原則。
下面的代碼就定義了一個依賴於alpha模塊的匿名模塊:
define(["alpha"], function (alpha) {
return {
verb: function(){
return alpha.verb() + 2;
}
};
});

3.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並強制載入依賴的模塊。

5.結論

AMD 規範是JavaScript開發的一次重要嘗試,它以簡單而優雅的方式統一了JavaScript的模塊定義和加載機制,並迅速得到很多框架的認可和採納。這對開發人員來說是一個好消息,通過AMD我們降低了學習和使用各種框架的門檻,能夠以一種統一的方式去定義和使用模塊,提高開發效率,降低了應用維護成本。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章