前端技術雖然在不斷髮展之中,卻一直沒有質的飛躍。除了已有的各大著名框架,好比Dojo,JQuery,ExtJs等等,不少公司也都有着本身的 前端開發框架。這些框架的使用效率以及開發質量在很大程度上都取決於開發者對其的熟悉程度,以及對JavaScript的熟悉程度,這也是爲何不少公司 的技術帶頭人都喜歡開發一個本身的框架。開發一個本身會用的框架並不難,但開發一個你們都喜歡的框架卻很難。從一個框架遷移到一個新的框架,開發者頗有可 能還會按照原有框架的思惟去思考和解決問題。這其中的一個重要緣由就是JavaScript自己的靈活性:框架沒辦法絕對的約束你的行爲,一件事情總能夠 用多種途徑去實現,因此咱們只能在方法學上去引導正確的實施方法。慶幸的是,在這個層面上的軟件方法學研究,一直有人在去不斷的嘗試和改 進,CommonJS就是其中的一個重要組織。他們提出了許多新的JavaScript架構方案和標準,但願能爲前端開發提供銀蛋,提供統一的指引。 html
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]); 瀏覽器
其中: 服務器
從中能夠看到,第一個參數和第二個參數都是能夠省略的,第三個參數則是模塊的具體實現自己。後面將介紹在不一樣的應用場景下,他們會使用不一樣的參數組合。 架構
從這個define函數AMD中的A:Asynchronous,咱們也不難想到define函數具備的另一個性質,異步性。當define函數 執行時,它首先會異步的去調用第二個參數中列出的依賴模塊,當全部的模塊被載入完成以後,若是第三個參數是一個回調函數則執行,而後告訴系統模塊可用,也 就通知了依賴於本身的模塊本身已經可用。若是對應到dojo1.6以前的實現,那麼在功能上能夠有以下對應關係: 框架
不一樣的是,在加載依賴項時,AMD用的是異步,而dojo.require是同步。異步和同步的區別顯而易見,前者不會阻塞瀏覽器,有更好的性能和靈活性。而對於NodeJs這樣的服務器端AMD,則模塊載入無需阻塞服務器進程,一樣提升了性能。 異步
下面代碼定義了一個alpha模塊,而且依賴於內置的require,exports模塊,以及外部的beta模塊。能夠看到,第三個參數是回調函數,能夠直接使用依賴的模塊,他們按依賴聲明順序做爲參數提供給回調函數。 ide
這裏的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(); } });
define 方法容許你省略第一個參數,這樣就定義了一個匿名模塊,這時候模塊文件的文件名就是模塊標識。若是這個模塊文件放在a.js中,那麼a就是模塊名。能夠在 依賴項中用"a"來依賴於這個匿名模塊。這帶來一個好處,就是模塊是高度可重用的。你拿來一個匿名模塊,隨便放在一個位置就可使用它,模塊名就是它的文 件路徑。這也很好的符合了DRY(Don't Repeat Yourself)原則。
下面的代碼就定義了一個依賴於alpha模塊的匿名模塊:
define(["alpha"], function (alpha) { return { verb: function(){ return alpha.verb() + 2; } }; });
前面提到,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並強制載入依賴的模塊。
Dojo 在三月初正式發佈了1.6版本,其中一個重要的變化就是引入了AMD機制,取代了原來的dojo.provide和dojo.require方法。可是現 在仍然保持了向後兼容性,你仍然能夠用dojo.provide和dojo.require來定義和加載模塊。須要注意的是:在 Dojo 1.6 中, 針對 AMD 的重構仍然屬於一個過渡期的改動 , 用戶本身開發的 AMD 模塊還不能被 Dojo 的加載器和 Build 系統支持 . 1.6 中現有的編譯系統對AMD的支持還很是侷限。 若是你本身開發了 AMD 格式的模塊,而且你仍然在使用默認的 Dojo 同步模塊加載器,那麼你必須嚴格遵循 Dojo 模塊的格式 ( 包括換行的格式 ) 來保證你本身的模塊可以成功編譯。總結起來有如下三點:
以Calendar爲例,用define方法來定義這個模塊:
define("dijit/Calendar", ["dojo", "dijit", "text!dijit/templates/Calendar.html", "dojo/cldr/supplemental", "dojo/date", "dojo/date/locale", "dijit/_Widget", "dijit/_Templated", "dijit/_CssStateMixin", "dijit/form/DropDownButton"], function(dojo, dijit) { dojo.declare( "dijit.Calendar", [dijit._Widget, dijit._Templated, dijit._CssStateMixin], {...} ); return dijit.Calendar; } );
能夠看到,模塊標識就是模塊文件的路徑,模塊自己通常都是dojo.declare定義的類。Dojo1.6中的dojo和dijit命名空間下的 模塊均已經用AMD的形式進行了重構,但dojox下仍然延用了傳統的dojo.provide和dojo.require形式。對AMD的引入是 Dojo走向自動化包管理的重要一步,在後續文章中咱們也將繼續關注Dojo在這方面的進展。
AMD 規範是JavaScript開發的一次重要嘗試,它以簡單而優雅的方式統一了JavaScript的模塊定義和加載機制,並迅速獲得不少框架的承認和採 納。這對開發人員來講是一個好消息,經過AMD咱們下降了學習和使用各類框架的門檻,可以以一種統一的方式去定義和使用模塊,提升開發效率,下降了應用維 護成本。
Dojo 中文博客
RequireJS/AMD Module Forms
Modules/AsynchronousDefinition