Dojo1.6新特性:再談AMD規範

來源:互聯網
上載者:User

 

1. AMD的由來

前端技術雖然在不斷髮展之中,卻一直沒有質的飛躍。除了已有的各大著名架構,比如Dojo,JQuery,ExtJs等等,很多公司也都有著自己的前端開發架構。這些架構的使用效率以及開發品質在很大程度上都取決於開發人員對其的熟悉程度,以及對JavaScript的熟悉程度,這也是為什麼很多公司的技術帶頭人都喜歡開發一個自己的架構。開發一個自己會用的架構並不難,但開發一個大家都喜歡的架構卻很難。從一個架構遷移到一個新的架構,開發人員很有可能還會按照原有架構的思維去思考和解決問題。這其中的一個重要原因就是JavaScript本身的靈活性:架構沒辦法絕對的約束你的行為,一件事情總可以用多種途徑去實現,所以我們只能在方法學上去引導正確的實施方法。慶幸的是,在這個層面上的軟體方法學研究,一直有人在去不斷的嘗試和改進,CommonJS就是其中的一個重要組織。他們提出了許多新的JavaScript架構方案和標準,希望能為前端開發提供銀蛋,提供統一的指引。

2. 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對象。

從中可以看到,第一個參數和第二個參數都是可以省略的,第三個參數則是模組的具體實現本身。後面將介紹在不同的應用情境下,他們會使用不同的參數組合。

從這個define函數AMD中的A:Asynchronous,我們也不難想到define函數具有的另外一個性質,非同步性。當define函數執行時,它首先會非同步去調用第二個參數中列出的相依模組,當所有的模組被載入完成之後,如果第三個參數是一個回呼函數則執行,然後告訴系統模組可用,也就通知了依賴於自己的模組自己已經可用。如果對應到dojo1.6之前的實現,那麼在功能上可以有如下對應關係:

  • module-name: dojo.provide
  • dependencies: dojo.require
  • module-factory: dojo.declare

不同的是,在載入依賴項時,AMD用的是非同步,而dojo.require是同步。非同步和同步的區別顯而易見,前者不會阻塞瀏覽器,有更好的效能和靈活性。而對於NodeJs這樣的伺服器端AMD,則模組載入無需阻塞伺服器處理序,同樣提高了效能。

3. 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();    }});
4. 匿名模組

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

下面的代碼就定義了一個依賴於alpha模組的匿名模組:

define(["alpha"], function (alpha) {    return {      verb: function(){        return alpha.verb() + 2;      }    };});
5. 僅有一個參數的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並強制載入依賴的模組。

6. Dojo中的AMD

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 模組的格式 ( 包括換行的格式 ) 來保證你自己的模組能夠成功編譯。總結起來有以下三點:

  • 用傳統的方法 (dojo.require()/dojo.provide()) – 這些模組,只能被 Dojo 同步載入器 載入,但可以被 Dojo 編譯系統(Build System )正確的編譯
  • 用 Dojo 同步載入器來載入 AMD 格式 ( define ()) 模組 – 這些模組可以被正常的載入,並且可以被其他相容 AMD 格式的載入器載入 . 現在雖然 Dojo1.6 還沒有正式支援這種用法, 但在目前的 Dojo1.6 編譯系統中,是可以正常工作的 . ( 前提是你必須嚴格遵循 Dojo 模組定義的代碼規範 )
  • 使用第三方載入器來載入 AMD 格式( define ())模組 – 模組可以被正常載入,並且可以被其他載入器所使用 . 這些模組可以使用 RequireJS 或 Backdraft 提供的編譯系統正常編譯,但是 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在這方面的進展。

7. 結論

AMD 規範是JavaScript開發的一次重要嘗試,它以簡單而優雅的方式統一了JavaScript的模組定義和載入機制,並迅速得到很多架構的認可和採納。這對開發人員來說是一個好訊息,通過AMD我們降低了學習和使用各種架構的門檻,能夠以一種統一的方式去定義和使用模組,提高開發效率,降低了應用維護成本。

參考資料:

Dojo 中文部落格
RequireJS/AMD Module Forms
Modules/AsynchronousDefinition

 

本文已經首發於InfoQ中文站,著作權,原文為《Dojo1.6新特性:再談AMD規範》,如需轉載,請務必附帶本聲明,謝謝。  
InfoQ中文站是一個面向中高端技術人員的線上獨立社區,為Java、.NET、Ruby、SOA、敏捷、架構等領域提供及時而有深度的資訊、高端技術大會如QCon 、線下技術交流活動QClub、免費迷你書下載如《架構師》等。

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.