Dojo 1.8:向完美架構繼續前行

jopen 12年前發布 | 10K 次閱讀 Dojo

Dojo 1.8已正式發布數星期,作為Dojo的鐵桿粉卻直到今天才來總結,實在心有不安,但這并不妨礙我們來看一看那些讓人眼前一亮的新特性。作為歷史最悠久的 RIA框架,Dojo的發展一直不冷不熱。比起后來者JQuery的大紅大紫,Dojo則默默的按照自己的步伐堅定的前行著。而對完美架構的追求,則構成了Dojo前行的主旋律,這也使得Dojo一直擁有一批堅定的支持者。

Dojo顯然并不重視設計靈巧的API,但卻非常看重編程思想的應用,以及對前端架構的研究。我們現在就來看看Dojo 1.8是如何在這個方向上繼續努力著。

1. 對匿名模塊的徹底支持:dojo/parser支持了對模塊ID的解析

從上個1.7版本開始,Dojo全面引入了AMD(Asynchronous Module Definition)的機制。這使得每個模塊的邏輯都完全擁有自己的閉包,通過返回值來暴露API接口或者數據。這樣的模塊是完全匿名的,AMD Loader會根據模塊ID(例如:dojo/html)來映射到路徑去尋找對應的文件。比起舊的Loader中每個模塊都需要聲明 dojo.provide('dojo.html'),AMD機制真正做到了DRY原則(Don't Repeat Yourself),模塊路徑就是唯一標識符,不再需要在模塊內部重復指定自身路徑。這樣的架構也讓Dojo幾乎可以做到完全沒有全局變量的存在,說幾乎是因為1.8之前有一個例外:通過Html聲明方式創建的widget仍然需要一個全局的data-dojo-type屬性:

<input type="text" data-dojo-type="dijit.form.TextBox"/>

這里的dijit.form.TextBox就是一個全局變量,表示一個TextBox控件的類名。在原來的dijit/form/TextBox模塊中,必須有一個定義:

declare('dijit.form.TextBox', [], {});

dojo的parser會根據這個Widget的data-dojo-type去找對應的實現類。所以,盡管作為AMD模塊已經是匿名,但其卻有一個隱藏的全局變量在模塊內定義,導致其自身其實并不是真正意義上的匿名,即不是完全的DRY。

然而在1.8中,這最后一個局限也完全消失了。dojo/parser能夠根據模塊ID來找到TextBox的實現,現在我們可以這樣聲明一個Widget:

<input type="text" data-dojo-type="dijit/form/TextBox"/>

盡管表面上看來,只是把點(.)換成了斜杠(/),但從編程的思想上來看,則可以讓模塊減少了一個概念:一個Widget就只有一個標識,就是模塊ID。而不是之前既有模塊ID,又有Widget類名這樣容易混淆的情形。概念上的簡單和清晰是理解易維護的重要前提。

本質上來看,AMD取代傳統的dojo.require無關乎性能,僅關乎于架構。AMD能讓你沿著正確的路線開發松耦合的模塊,讓應用更加容易理解和維護。而Dojo正在不斷的完善著細節,幫助我們設計出更好的應用架構。

2. 讓每個Widget都有插件機制:新的data-dojo-mixins屬性

插件機制是建立靈活可擴展應用的一個最佳實踐,而現在Dojo通過這個全新的屬性全面實現了插件機制,讓Dojo的Widget在使用時可以靈活決定自己需要的特性。因為這個屬性的存在,Widget的開發也將可以更加模塊化,每一組功能都能獨自定義。在使用的時候,根據具體的使用場景,來決定是否啟用此功能。比如,在dojox中提供了最近較為流行的TreeMap組件。模塊dojox/treemap/TreeMap本身僅僅包含了最基礎的功能。而對于鍵盤支持,色塊拆分等功能則通過可插拔的模塊來實現:dojox/treemap/Keyboard和dojox/treemap /DrillDownUp模塊。這樣的附屬模塊可以理解為插件,按照需要將其添加到data-dojo-mixins屬性中即可開啟相應的功能。

<div data-dojo-type="dojox/treemap/TreeMap" data-dojo-mixins="dojox/treemap/Keyboard, 
dojox/treemap/DrillDownUp"></div>

在這里,data-dojo-mixins屬性中用逗號隔開的模塊就可以看成一個個插件。不僅新控件可以利用插件機制,為現有組件添加新的功能,也可以通過提供插件來實現。比起派生一個新類來實現此功能,插件是可以通用的。例如:假設要為dijit/form下的TextBox,Select等表單控件添加語音識別的功能,可能只需要寫一個my/voice/Recognizer模塊,這樣具有set('value', value)這樣接口的Widget都將能靈活選用這個組件,而不需要為每個Widget都派生一個新的類。而對于多個插件的功能組合,則更顯然 data-dojo-mixins會非常合適。

現在看到的是聲明方式創建的Widget我們可以很好的利用data-dojo-mixins實現插件機制。那么對于動態創建的Widget呢?其實這1.8之前已經可以實現:

var treemap = new (declare(['dojox/treemap/TreeMap', 'dojox/treemap/Keyboard',
 'dojox/treemap/DrillDownUp']))(arguments);

模塊的靈活性,一直是Dojo的重點關注。從引入AMD開始,Dojo就提出了base-less的概念,即Dojo框架可以配置為沒有任何核心庫,所有的模塊都按需加載。這要求每個模塊提供的功能獨立而精簡,從而能夠實現最終僅僅加載需要的代碼的目的。而data-dojo-mixins屬性則是可以幫助我們將功能拆分并模塊化到極致。這在目前JavaScript代碼普遍臃腫的大環境下無疑是一個讓人眼前一亮的概念和做法。

3. 契約式編程:dojo/promise

契約式編程是另一種很好的編程實踐,它的目標是讓程序的各個模塊各司其職,僅僅關注自己需要完成的事情,而不用去關心其它模塊該關心的事情。從而可以降低模塊之間的耦合度,同時也讓程序在語義上更加容易理解。其概念已經非常成熟,讀者可以搜索相關資料,這里不再贅述。

雖然在1.8之前,Dojo可以通過dojo.Deffered實現契約編程的思想,但是終歸不是很正式。而從1.8起,Dojo提供了正式的類和API來全面支持契約式編程:

dojo/promise/Promise - 契約核心類,所有實現契約機制的類都能夠提供一個Promise類的實例。比如dojo/Deffered,專門對應于異步的情形。

dojo/errors/CancelError - 當一個契約被未知原因的取消時,提供此默認錯誤。

dojo/promise/all - 接受多個契約作為參數,返回一個新的契約。只有當多個契約都得到滿足時,新契約才會得到滿足。從本質看,這取代了原來的dojo/DeferredList。

dojo/promise/first - 接受多個契約作為參數,返回一個新的契約。只要其中一個契約得到滿足時,新契約立刻滿足。

舉例:

require(["dojo/promise/all", "dojo/Deferred", "dojo/dom", "dojo/on", "dojo/json", "dojo/domReady!"],
function(all, Deferred, dom, on, JSON){
  function googleRequest(){
    var deferred = new Deferred();
    setTimeout(function(){
      deferred.resolve("foo");
    }, 500);
    return deferred.promise;
  }
  function bingRequest(){
    var deferred = new Deferred();
    setTimeout(function(){
      deferred.resolve("bar");
    }, 750);
    return deferred.promise;
  }

  function baiduRequest(){
    var deferred = new Deferred();
    setTimeout(function(){
      deferred.resolve("baz");
    }, 1000);
    return deferred.promise;
  }
  on(dom.byId("startButton"), "click", function(){
    dom.byId("output").innerHTML = "Running...";
    all([googleRequest(), bingRequest(), baiduRequest()]).then(function(results){
      dom.byId("output").innerHTML = JSON.stringify(results);
    });
  });
});

通過這段代碼可以看到,對每個搜索引擎的搜索請求都返回一個契約,外界程序只需關心這個契約。在搜索完成之后,契約會通過調用resolve方法來告訴外界自己已完成任務。從而外界僅需要關心什么時候開始搜索,以及搜索完成后自己該做什么。其邏輯和語義都非常清楚和合理。通過promise提供的 all,first方法,外界可以更靈活的對契約進行管理。

通過promise,請求發起和結果處理過程被嚴格的區分開來,你將不得不分開代碼處理邏輯,這種隱含的硬性規定讓代碼程序結構更加良好,代價是會讓初用者覺得上手困難,但是一旦習慣,帶來的好處將是一勞永逸的。

4. 更為統一的IO模塊:dojo/request

所有的RIA框架都會通過隱藏底層細節,提供統一的一致的API來實現跨瀏覽器的支持。而現在Dojo正在將跨瀏覽器擴展到跨JavaScript環境。 dojo/request正是有這樣需求的一個API。在瀏覽器端,通過XMLHttpRequest獲取數據,而在NodeJs之類 JavaScript環境則用NodeJS提供的文件系統API來實現。

dojo/request 這個package引入了一種用于異步請求的全新架構。這個模塊將用戶從具體的請求細節中抽象出來,也就是說,用戶無需關心請求是如何發生的。 dojo/request正是基于上文提到的dojo/promise來構建的。當引入dojo/request模塊時,將根據運行平臺自動返回對應的實現。比如,瀏覽器中就會使用dojo/request/xhr,而NodeJS平臺則會使用dojo/request/node。

下面的代碼演示了dojo/request的基本用法:

require(["dojo/request"], function(request){  
    var promise = request(url, options);  
    promise.then(  
        function(data){  
        },  
        function(error){  
        }  
    );  
    promise.response.then(  
        function(response){  
        },  
        function(error){  
        }  
    );  
    request.get(url, options).then(...);  
    request.post(url, options).then(...);  
    request.put(url, options).then(...);  
    request.del(url, options).then(...);  
});

詳細的dojo/request的介紹大家可以參考Dojo中文博客的文章:深入淺出dojo/request: http://blog.csdn.net/dojotoolkit/article/details/7991286

5. 面向移動設備的全面支持:dojo/touch,dojox/mobile

dojo/touch 模塊的目的是為了讓面向PC的Web應用也能夠運行在移動設備上,它提供了許多鍵盤鼠標事件和手勢操作的映射。從而讓普通的控件在移動設備上也能正常工作。現在Dojo自帶的控件基本都采用了dojo/touch來實現對移動設備的支持。對于自定義的控件,也可以通過dojo/touch模塊來實現這一功能。

Mobile 是最近的熱點領域,HTML5能夠讓同一份代碼運行在不同的平臺上。雖然有些類型的應用并不適合使用HTML5,但大多數信息展示和簡單交互的移動應用非常適合使用HTML5,能夠大大減少開發維護成本。而Dojo 1.8在對Mobile設備提供了大力的支持,提供了多達28個全新的控件,比如 TreeView,ScrollablePane,DatePicker,GridLayout等常用控件。通過PhoneGap+Dojo把HTML5 封裝成手機應用,將是一個完全免費的開源跨平臺移動開發解決方案。這也正是IBM使用的解決方案。

小結

Dojo 一直非常看重編程思想的使用,以及前端架構的優化。比如1.8中的契約式編程,AMD增強,等等。雖然提供了大量新的API,但是為了保證向后兼容仍然保留了原有的API,只是會標識為已過期。這些過期的API將會在明年發布的2.0版本中徹底刪除。這在一定程度增加了升級的難度,但是為了軟件的健壯和可維護性,這是值得付出的代價。1.8和1.9版本也使得到2.0的過渡更加平滑。總體來說,我相信隨著大家對Dojo的深入了解,Dojo一定會出現在越來越多的大型項目之中。

參考資源:

Dojo 1.8 下載:http://dojotoolkit.org

Dojo 1.8 Release Notes: http://dojotoolkit.org/reference-guide/1.8/releasenotes/1.8.html

Dojo中文博客:http://blog.csdn.net/dojotoolkit

Dojo新浪微博:http://weibo.com/dojotoolkit

本文首發于infoQ,原文鏈接:http://www.infoq.com/cn/articles/dojo-1.8

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!