一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

使用 Dojo 的 Ajax 應用開發(fā)進階教程,第 8 部分: Dijit 開發(fā)最佳實踐

 javaxiaop 2010-10-12

Dijit 組件(widget)是 Dojo 提供的圖形用戶界面組件庫。它提供了 Ajax 應用開發(fā)中會用到的常用組件,可以幫助開發(fā)人員快速的構建 Ajax 應用。本文并不會介紹 Dojo 默認提供的組件,而是側重于介紹 Dijit 組件的編程模型和最佳實踐,其目的是幫助開發(fā)人員更好的開發(fā)自己的 Dijit 組件。下面首先對 Dijit 做概要介紹。

Dijit 概述

Dijit 組件的存在是 Dojo 框架區(qū)別于其它 JavaScript 框架的一個重要特性。在桌面應用開發(fā)中,開發(fā)人員大量使用圖形用戶界面組件庫來提高開發(fā)效率。而在 Web 應用開發(fā)中,HTML 語言本身僅提供了少數(shù)基本的控件,如按鈕、單選框、復選框、文本輸入框和下拉列表等。而對于在 Web 應用開發(fā)中常見的一些復雜組件,如對話框、菜單、工具欄、進度條、富文本編輯器和樹等,并沒有提供原生的支持。在這種情況下,開發(fā)人員往往需要自己開發(fā)這樣的復雜組件,這就造成了更長的開發(fā)周期和更高的開發(fā)和維護的成本。

Dojo 提供了一個種類多樣的組件庫。開發(fā)人員只需要簡單的定制就可以在自己的應用中使用這些組件。除此之外,Dojo 還提供了完善的組件編程模型。如果默認提供的組件都不能滿足需求,可以自己來開發(fā)所需的組件。遵循這個統(tǒng)一的編程模型,會比從頭開始創(chuàng)建組件要容易得多。有了組件的概念之后,開發(fā)人員在設計 Web 應用的時候,就可以從比較高的抽象層次來對應用的各個部分進行劃分。定義好清晰的組件接口之后,團隊成員就可以各司其職,并行開發(fā),從而提高開發(fā)效率。

在開發(fā) Dijit 組件的時候,需要注意下面幾個基本的問題。

  • 組件的粒度問題。一般來說,功能比較復雜的組件不利于復用,也不利于團隊開發(fā)時的分工合作。但是過多小組件在頁面上的時候,會消耗比較多的系統(tǒng)資源,影響性能。而性能對 Web 應用來說是一個非常重要的因素。因此需要進行一定的權衡。比較好的做法是從較大的組件開始,當發(fā)現(xiàn)存在代碼重復的時候,再把重復的代碼提取出來,重構成新的組件。這樣就把劃分成小組件的決策推遲到了真正需要的時候,避免過度設計。
  • 組件的接口問題。組件的接口定義了代碼中的其它部分如何使用該組件。一般來說,組件可以提供三類的接口:公共屬性、公共方法和事件綁定點。公共屬性指的是組件提供的可以公開訪問的簡單數(shù)據(jù)類型屬性。一般在創(chuàng)建組件的時候使用,用來對組件進行定制;公共方法指的是可以公開訪問的 JavaScript 方法。一般在組件創(chuàng)建完成之后使用,用來改變組件的行為;事件綁定點是組件暴露出來的占位方法。一般由組件使用者通過 dojo.connect()來綁定到該方法上。組件使用該方法來通知使用者其內部狀態(tài)的變化。這類方法一般以 on作為名稱前綴。開發(fā)人員應該根據(jù)需要定義合適的接口,避免一些不好的實踐。比如公共屬性的值在組件創(chuàng)建之后,一般不推薦使用者設置其值。如果設置該屬性的值是一個合理的場景的話,最好提供相應的公共方法,并以文檔的形式告訴使用者正確的用法。
  • 組件之間的交互問題。組件之間如果需要相互通訊的話,最好使用組件的對象引用來完成。比如某個組件在創(chuàng)建另外一個組件的時候,可以把自己的對象引用作為參數(shù)傳遞給其創(chuàng)建出來的組件。后者就可以使用此對象引用來調用前者的方法。另外一種做法是通過 dojo.publish()dojo.subscribe()方法來完成。這種做法的使用比較簡單,可以避免層次較深的對象引用傳遞。不好的地方是組件之間的關聯(lián)關系不夠清晰,也比較難維護。推薦的做法是優(yōu)先使用第一種方式。

 

上面對開發(fā) Dijit 組件的一些通用問題進行了討論。下面開始介紹 Dijit 組件的編程模型。在編程模型的介紹過程中,會穿插介紹相關的最佳實踐。首先從 Dijit 組件的核心類 dijit._Widget開始。


dijit._Widget

dijit._Widget是所有 Dijit 組件的父類。Dijit 默認提供的組件以及自己開發(fā)的組件都需要繼承自此類。dijit._Widget所提供的方法涉及組件的生命周期、屬性設置和獲取、事件處理和其它輔助功能等。深入了解該類的這些方法的用法和實現(xiàn)細節(jié),是開發(fā)自己的 Dijit 組件的基礎。下面分別對 dijit._Widget提供的方法進行分類討論。

組件生命周期

dijit._Widget提供了對組件生命周期的完整管理,包括組件的創(chuàng)建和銷毀。Dijit 組件的生命周期管理在實現(xiàn)的時候,使用了模板方法(Template Method)設計模式。dijit._Widget類的 create()方法定義了 Dijit 組件創(chuàng)建時的生命周期的默認模板。該方法會在合適的時機調用模板中包含的其它方法。這些不同的方法構成了組件生命周期中的各個階段。開發(fā)自己的組件的時候,可以覆寫其中的某些方法,從而在感興趣的階段添加自己的處理邏輯。開發(fā)人員也可以覆寫 create()方法來提供一套完全不同的生命周期實現(xiàn)。不過這種做法風險太大,不建議使用。絕大多數(shù)情況下,覆寫默認模板提供的方法就足夠了。dijit._Widget中定義的組件生命周期中的創(chuàng)建階段如 圖 1 所示。


圖 1. Dijit 組件創(chuàng)建過程
Dijit 組件創(chuàng)建過程

圖 1 所示,創(chuàng)建 Dijit 組件時的過程中包含如下幾個步驟:

  1. 以聲明式或是編程式的方式創(chuàng)建 Dijit 組件。
  2. 創(chuàng)建時傳入的參數(shù)被混入(mixin)到當前 Dijit 組件對象中?;烊胪瓿芍?,postMixInProperties()方法被調用。
  3. 如果沒有為此組件對象提供 ID 的話,則自動生成一個惟一的 ID。把此對象添加到全局的組件注冊表中。完成之后,buildRendering()方法被調用。
  4. 設置 Dijit 組件的屬性。完成之后,postCreate()方法被調用。
  5. 當組件被添加到頁面上之后,顯式調用 startup()方法。
  6. 組件創(chuàng)建完成之后,開始正常工作。

 

圖 1 中給出了 4 個方法的名稱,其中橢圓形中的 postMixInProperties()、buildRendering()postCreate()dijit._Widget提供的組件生命周期中的擴展點。自己開發(fā)的組件應該通過覆寫這些方法來實現(xiàn)自己的邏輯。圓角矩形中的 startup()方法并不是創(chuàng)建過程中的一部分,需要在 Dijit 組件創(chuàng)建完成之后顯式的調用。下面對這 4 個方法進行詳細的說明。

  • postMixInProperties():在創(chuàng)建 Dijit 組件的時候,可以通過參數(shù)來傳入一個包含各種屬性的 JavaScript 對象。這些屬性被混入到 Dijit 組件中,在代碼中可以通過 this來引用。當混入完成之后,在創(chuàng)建組件的界面之前,可能還需要執(zhí)行一些處理。這些處理的邏輯可以添加在 postMixInProperties()方法中。
  • buildRendering():該方法用來創(chuàng)建 Dijit 組件的用戶界面,即 DOM 節(jié)點。該方法完成之后,Dijit 組件的 this.domNode指向的是創(chuàng)建完成的 DOM 節(jié)點。
  • postCreate():當 Dijit 組件的 DOM 節(jié)點創(chuàng)建完成之后,此方法被調用。需要注意的是,這個時候組件的 DOM 節(jié)點可能還沒有被添加到當前頁面文檔樹中。
  • startup():當 Dijit 組件及其子組件被創(chuàng)建完成并添加到當前頁面文檔樹中之后,顯式的調用此方法。該方法對于那些包含子組件的 Dijit 組件來說非常有用,可以用來控制子組件和進行布局。startup()方法只需要調用一次即可。調用完成之后,組件的屬性 _started的值為 true,可以用來判斷 startup()方法是否已經(jīng)被調用過。

 

與創(chuàng)建 Dijit 組件對應的組件的銷毀過程。銷毀過程比較復雜,涉及到 5 個方法,如 圖 2 所示。圖 2 中的箭頭表示的是方法之間的調用關系。


圖 2. Dijit 組件銷毀過程
Dijit 組件銷毀過程

圖 2 所示,最常用的銷毀一個 Dijit 組件的方法是 destroyRecursive()。該方法用來銷毀一個 Dijit 組件及其包含的子組件。該方法會首先調用 destroyDescendants()方法來銷毀子組件。由于子組件也可能包含自己的子組件,destroyDescendants()也會調用 destroyRecursive()方法來刪除其子組件,從而形成一個遞歸的銷毀過程。當子組件銷毀完成之后,destroyRecursive()會調用 destroy()方法來銷毀自己。destroy()方法會首先調用 uninitialize(),接著執(zhí)行內部的清理工作,最后調用 destroyRendering()方法來銷毀組件的 DOM 節(jié)點。需要注意的是,destroy()方法會銷毀在 Dijit 組件模板中定義的子 Dijit 組件。這 5 個方法中除了 uninitialize()之外的其它 4 個方法,都有一個參數(shù) preserveDom用來表明是否保留 Dijit 組件的 DOM 節(jié)點,不過對從模板創(chuàng)建出的 Dijit 組件無效。

在深入理解了 Dijit 組件的生命周期之后,就可以更好的利用 Dijit 庫提供的支持來開發(fā)自己 Dijit 組件。下面介紹一些與組件生命周期相關的最佳實踐。

  • 靈活覆寫 Dijit 組件生命周期相關的方法來添加自己的處理邏輯。一般來說,在 postCreate()方法中添加組件相關的處理邏輯即可。如果需要添加與 DOM 節(jié)點大小和位置相關的邏輯,應該放在 startup()方法中,并要求組件的使用者顯式調用。在覆寫方法的時候,要通過 this.inherited(arguments);來調用 dijit._Widget類的原始方法。
  • 如果自己的 Dijit 組件在銷毀的時候需要執(zhí)行額外的處理,應該把相關的邏輯添加在 uninitialize()方法中,并且只包含當前組件相關的邏輯,不需要考慮子組件。destroyRecursive()方法會負責處理子組件的銷毀。
  • 總是使用 destroyRecursive()來銷毀一個 Dijit 組件。這樣可以確保子組件總是被正常銷毀,避免內存泄露。
  • 盡量不要覆寫 destroyRecursive()、destroyDescendants()、destroy()destroyRendering()等 4 個方法。這 4 個方法封裝了完整的 Dijit 組件銷毀邏輯。一般來說,覆寫 uninitialize()方法就已經(jīng)足夠了。

 

屬性獲取與設置

Dijit 組件中可能包含各種不同的屬性,允許使用者對這些屬性進行獲取和設置。下面以一個顯示電子郵件地址的 Dijit 組件來進行說明。該組件應該允許使用者獲取和設置顯示的電子郵件地址。一般來說,需要提供 getEmail()setEmail(email)兩個方法來實現(xiàn)。另外,為了防止暴露郵件地址,一般需要用特殊的字符替換掉電子郵件地址中的“@”符號,如 admin@example.org被替換成 admin#example.org。這樣的話就需要提供另外的兩個方法。為了簡化屬性的獲取和設置操作,dijit._Widget類中提供了 attr(name, value)方法來統(tǒng)一完成屬性的獲取和設置。當只傳入一個參數(shù)的時候,如果該參數(shù)是一個字符串,則表示獲取屬性的值;如果參數(shù)是一個 JavaScript 對象,則用該對象中的屬性和值來設置組件的屬性。當傳入兩個參數(shù)的時候,兩個參數(shù)分別表示為屬性的名稱和要設置的值。對于示例 Dijit 組件來說,可以通過 attr("email", "alex@example.com")來設置要顯示的電子郵件地址。

對于 Dijit 組件自定義的屬性,默認情況下是保持在組件對象實例中的。attr()方法直接讀取和設置組件對象中對應屬性的值即可。除此之外,還可以通過 attributeMap來提供從屬性到 DOM 節(jié)點之間的映射。即通過改變屬性的值,就可以修改 DOM 節(jié)點。如果希望在獲取和設置屬性的時候添加額外的處理邏輯,則需要提供滿足命名規(guī)范的對應方法。該命名規(guī)范是在首字母大寫的屬性名稱上添加特定的前綴和后綴。如對于屬性 email來說,自定義的獲取和設置屬性的方法分別是 _getEmailAttr()_setEmailAttr()。自定義方法的優(yōu)先級高于默認的方法。attr()會首先嘗試在 Dijit 組件對象中查找是否存在自定義的方法。代碼清單 1 中給出了一個獲取和設置屬性的示例。


清單 1. 屬性獲取和設置示例
 dojo.declare("emailDisplayer", dijit._Widget, {
            replaceString : "",
            _setEmailAttr : function(value) {
            if (!value) {
            return;
            }
            var originalValue = value;
            var displayValue = value;
            if (this.replaceString) {
            displayValue = displayValue.replace(/@/, this.replaceString);
            }
            this.domNode.innerHTML = displayValue;
            this.email = originalValue;
            }
            });
            var n = dojo.create("div", null, dojo.body());
            var displayer = new emailDisplayer({}, n);
            displayer.attr("replaceChar", "#");
            displayer.attr("email", "alex@example.org");
            displayer.attr("email");   

代碼清單 1 所示,Dijit 組件 emailDisplayer定義了兩個屬性 emailreplaceString。對于屬性 email提供了自定義的設置方法 _setEmailAttr()。對于屬性 replaceString則使用的是默認的實現(xiàn)。

對于統(tǒng)一的 attr()方法接口和自定義的屬性獲取和設置方法,Dijit 組件既可以方便使用者的使用,又給了開發(fā)人員足夠的靈活性。在組件開發(fā)中,盡量避免提供 Dijit 組件自己的屬性獲取和設置方法,而是充分利用 Dijit 組件提供的支持。

其它方法

除了上面提到的兩類方法之外,dijit._Widget還提供了其它一些方法。

  • 盡量使用 connect()方法來綁定事件處理方法。其好處是在 Dijit 組件被銷毀的時候,會自動調用 disconnect()來取消事件綁定。
  • 盡量使用 subscribe()方法來監(jiān)聽事件通知。其好處是在 Dijit 組件被銷毀的時候,會自動調用 unsubscribe()來取消事件監(jiān)聽。

 

在介紹完 dijit._Widget之后,下面介紹另外一個核心類 dijit._Templated


dijit._Templated

在介紹 Dijit 組件的生命周期的時候,提到過 dijit._Widget類中的 buildRendering()方法用來創(chuàng)建 Dijit 組件的用戶界面。如果通過 DOM 操作來創(chuàng)建用戶界面的話,一般來說會比較復雜,維護起來也比較麻煩。dijit._Templated提供了一種從 HTML 模板中創(chuàng)建 Dijit 組件用戶界面的方式。dijit._Templated一般是作為一個混入類的方式來使用的。它提供了自己的 buildRendering()用來從模板字符串或是文件中創(chuàng)建 DOM 節(jié)點。在使用 dijit._Templated的時候,有下面幾點需要注意。

  • 通過 dojo.declare()方法定義新的組件的時候,dijit._Widget需要作為基類,而 dijit._Templated只能作為混入類。也就是說在父類聲明中,dijit._Widget需要作為第一個出現(xiàn);否則的話會出現(xiàn)錯誤。
  • 可以通過 templateStringtemplatePath兩種方式來指定所使用的模板。從 Dojo 1.4 開始,建議使用 templateString來指定模板字符串。不過在 Dijit 組件開發(fā)過程中,把模板存放在單獨的 HTML 文件中更加便于開發(fā)和調試。因此在開發(fā)過程中可以使用 templatePath。在發(fā)布的時候,則需要通過 Dojo 的構建過程把 HTML 文件的內容內聯(lián)到組件代碼中,通過 templateString來表示。
  • 通過設置屬性 widgetsInTemplate的值為 true可以聲明該 Dijit 組件中包含其它的組件。這些包含的組件在 destroy()方法中會被銷毀。

 

在模板中可以使用 dojoAttachPointdojoAttachEvent兩個特殊的 DOM 節(jié)點的屬性。dojoAttachPoint屬性的值被轉換成組件對象中的一個屬性的名稱,該屬性的值是當前的 DOM 節(jié)點。dojoAttachEvent屬性的值被轉換成通過 dojo.connect()完成的事件處理綁定。如 <div dojoAttachPoint="myDiv" dojoAttachEvent="onclick:show" />聲明了該 DOM 節(jié)點可以在組件對象中通過 myDiv來引用,點擊該節(jié)點會調用 show()方法。這兩個屬性的存在帶來了編程上的簡便。在組件對象的方法中,如果需要引用某個 DOM 節(jié)點的話,一般需要通過 DOM 查詢或是 dojo.query()來完成,這樣的話會比較繁瑣。通過 dojoAttachPoint就避免了查詢操作,使用起來更加簡單。如果需要綁定事件處理的話,使用 dojoAttachEvent就免去了對 dojo.connect()方法的顯式調用。不過使用這兩個屬性的話,會造成變量的聲明和使用在不同的地方,會在一定程度上影響代碼的可讀性。比較好的實踐如下:

  • 為了方便區(qū)分組件對象中通過 dojoAttachPoint聲明的屬性和一般的屬性,最好為 dojoAttachPoint聲明的屬性名稱添加統(tǒng)一的后綴,如 Node或是 Container。其好處是開發(fā)人員在發(fā)現(xiàn)帶某個后綴的屬性時,會明白要去模板中查找相關的聲明。
  • 在引用 DOM 節(jié)點和綁定事件處理方法的時候,盡量使用統(tǒng)一的方式??梢越y(tǒng)一使用 dojoAttachPointdojoAttachEvent,也可以統(tǒng)一使用 DOM 查詢和 dojo.connect()。最好不要兩種方式混用。項目開發(fā)團隊應該根據(jù)團隊的意見,制定出相關的代碼編寫規(guī)范。

 

在介紹完 dijit._Templated之后,下面介紹另外一個核心類 dijit._Container。


dijit._Container

有些 Dijit 組件是作為其它組件的容器而存在的,如與頁面布局相關的組件。作為容器的組件需要對其包含的子組件進行管理,包括查詢、添加和刪除子組件等。dijit._Container混入類提供了這些管理子組件的功能,自己開發(fā)的組件可以直接混入此類。dijit._Container所提供的方法如下所示:

  • addChild(widget, insertIndex):該方法用來添加一個新的子組件到給定位置上。參數(shù) widget表示的是子組件,insertIndex表示的是添加的位置。
  • removeChild(widget):該方法用來移除一個子組件。參數(shù) widget既可以是子組件的引用,也可以是子組件的序號。
  • getChildren():該方法返回一個包含所有子組件的數(shù)組。
  • hasChildren():該方法用來判斷是否包含子組件。
  • getIndexOfChild(widget):該方法用來返回一個子組件的序號。

 

通過上面提到的這些方法,就可以完成對子組件的管理。在使用 dijit._Container的時候,有下面幾點需要注意:

  • dijit._Container中只能包含 Dijit 組件,也就是必須繼承自 dijit._Widget。不能包含普通的 DOM 節(jié)點。對于 DOM 節(jié)點,可以用一個 dijit.layout.ContentPane封裝之后,再添加到 dijit._Container中。
  • dijit._Container的子組件的 DOM 節(jié)點都是屬性 containerNode所表示的 DOM 節(jié)點的子節(jié)點。而 containerNode的值與 domNode不一定相同。只有 containerNode中包含的子組件才會在 destroyDescendants()方法中被銷毀。一般來說,在 Dijit 組件的 domNode下指定一個 DOM 節(jié)點作為 containerNode。
  • 由于 dijit._Container負責管理其中包含的子組件,其 startup()方法會負責調用子組件的 startup()方法。對于通過 addChild()方法動態(tài)添加的子組件,如果子組件的 startup()方法沒有被調用過,則會調用此 startup()方法。
  • removeChild()只是將子組件移除,使其不再受 dijit._Container的管理,并不會銷毀該子組件。

 

在介紹完 dijit._Container之后,下面介紹如何對 Dijit 組件進行管理。


Dijit 組件管理

Dojo 會負責維護當前頁面上所有 Dijit 組件的一個注冊表,里面包含了所有的 Dijit 組件對象。該注冊表可以通過 dijit.registry來訪問,它是一個 dijit.WidgetSet類的實例。dijit.WidgetSet實際上是一個 Dijit 組件的 ID 及其對象對應的查找表。通過組件的 ID 就可以查詢到組件對象。dijit.WidgetSet所包含的方法用來對此查找表進行操作。這些方法包括:

  • add(widget)用來添加新組件 widget;remove(id)用來根據(jù) ID 移除組件。
  • byId(id)用來通過 ID 查找組件;byClass(cls)用來根據(jù)類名來查找組件,如 dijit.registry.byClass("dijit.form.Button")用來查找頁面上所有的 dijit.form.Button組件。該方法的返回值是一個新的 dijit.WidgetSet對象。
  • toArray()返回一個包含所有組件對象的數(shù)組。
  • forEach()、filter()map()、every()some():這些方法的含義與用法與 Dojo 基本庫中處理數(shù)組的同名方法的含義與用法是相同的,都是用來對所包含的組件對象進行處理。

 

除了全局的組件注冊表 dijit.registry之外,Dijit 庫還提供了其它的方法。這些方法包括:

  • dijit.byId(id)用來根據(jù) ID 在頁面上查找組件。
  • dijit.getUniqueId(widgetType)用來為指定組件類別 widgetType中的新組件生成惟一的 ID。
  • dijit.findWidgets(root)用來查找指定 DOM 節(jié)點 root中所包含的 Dijit 組件。但是不包括嵌套的 Dijit 組件。
  • dijit.getEnclosingWidget(node)用來查找包含 DOM 節(jié)點 node的 Dijit 組件。

 

在管理 Dijit 組件的時候,有下面幾個問題需要注意:

  • 當需要管理一些 Dijit 組件的時候,可以創(chuàng)建自己的 dijit.WidgetSet對象。這樣就可以利用 dijit.WidgetSet所提供的管理功能。
  • dijit.registry是通過組件的 ID 來進行查找的,因此要求 ID 是全局惟一的。對于系統(tǒng)生成的 ID,是可以保證其惟一性的。不過開發(fā)人員也可以為組件提供自己的 ID,這個時候就需要格外注意 ID 的惟一性。如果試圖創(chuàng)建一個 ID 為 myId的組件,但是頁面上已經(jīng)存在 ID 相同的組件,Dojo 會拋出一個異常 "Tried to register widget with id==myId but that id is already registered"。造成這種情況的原因比較多:第一種可能是由于編程失誤造成了 ID 重復,這種情況下只需要修改重復的 ID 即可;另外的可能是準備復用某個 ID,但是前一個 Dijit 組件的銷毀不徹底,并沒有從全局組件注冊表 dijit.registry中移除掉自己。當再次使用此 ID 創(chuàng)建組件的時候就出現(xiàn)了錯誤。在 dijit._Widget類的 destroy()方法中包含了從 dijit.registry中刪除當前組件的實現(xiàn)。如果自己開發(fā)的組件覆寫了 destroy()方法,而沒有通過 this.herited(arguments)來調用父類的邏輯的話,就很容易出現(xiàn)這樣的錯誤。

 

在介紹完 Dijit 組件管理之后,下面介紹實例化 Dijit 組件的兩種方式。


實例化 Dijit 組件

一般來說,實例化 Dijit 組件有兩種方式:聲明式和編程式。聲明式的方式指的是在 HTML 代碼中以描述的方式來定義 Dijit 組件,由 Dojo 在運行時刻把這些聲明的組件實例化。編程式的方式是開發(fā)人員在代碼中顯式的通過 new操作符來實例化某個 Dijit 組件。實例化一個 Dijit 組件需要兩個參數(shù),第一個是混入到組件實例中的包含配置屬性的 JavaScript 對象,第二個則是組件所使用的 DOM 節(jié)點。這兩種方式的不同之處在于如何提供這兩個參數(shù)的值。下面首先介紹聲明式的方式。

聲明式

使用聲明式的時候,需要在 DOM 節(jié)點上添加屬性 dojoType,其值是 Dijit 組件類的全名。這樣就聲明了在運行時刻會從此 DOM 節(jié)點上創(chuàng)建一個新的 Dijit 組件。如 <div dojoType="dijit.form.Button" />聲明了會從此 div元素上創(chuàng)建一個 dijit.form.Button組件。而對于混入到組件實例中的屬性,則是通過此 DOM 節(jié)點上的屬性值來聲明的。由于在 HTML 代碼中聲明屬性的時候只能使用字符串,而 Dijit 組件中的屬性是有數(shù)據(jù)類型的,因此需要一個轉換的過程。了解此過程有助解決一些屬性無法設置的問題。這個轉換過程具體如下:

  1. 對于通過 dojoType聲明的組件類,遍歷該類的 prototype對象中的屬性,去掉以“_”開頭的和 Object.prototype中包含的屬性,其余的就是可以在聲明 Dijit 組件的時候使用的屬性。這些屬性的值的數(shù)據(jù)類型也會被記錄下來。
  2. 接著對于上一步中得到的每個屬性,查看 DOM 節(jié)點是否包含有同名屬性。如果有的話,則得到此屬性的值,并根據(jù)數(shù)據(jù)類型進行轉換。
  3. 完成類型轉換之后的值被作為最終的結果。代碼清單 2 中給出了一個示例。

 


清單 2. 聲明式創(chuàng)建 Dijit 組件時屬性類型轉換示例
 dojo.declare("myWidget", dijit._Widget, {
            count : 1,
            valid : false,
            names : ["Alex", "Bob"]
            });
            <div dojoType="myWidget" count="20" names="John, Jason" />

代碼清單 2 給出的例子中,myWidget中定義了 3 個屬性:countvalidnames,其數(shù)據(jù)類型分別是數(shù)字、布爾型和數(shù)組。在聲明 Dijit 組件的時候,在 DOM 節(jié)點上添加了屬性 countnames。屬性 count的值被轉換成數(shù)字,而屬性 names的值被轉換成數(shù)組。

除了通過 DOM 節(jié)點的屬性來設置 Dijit 組件的屬性之外,還可以通過 <script>子元素來聲明方法。<script>元素的內容就是方法體本身。支持的聲明方式有三種:

  • <script type="dojo/method" event="myHandler">:聲明的 JavaScript 方法與其它簡單屬性一樣,被混入到組件對象中。屬性名稱是 event的值。
  • <script type="dojo/method" >:聲明的 JavaScript 方法在組件實例化之后會被立即執(zhí)行。
  • <script type="dojo/connect" event="onClick" >:聲明的 JavaScript 方法在組件實例化之后會通過 dojo.connect()綁定到 event所指明的事件上。
對于第一種方式,也是可以通過 DOM 節(jié)點上的屬性來進行聲明的。如 <div dojoType="myWidget" myHandler="alert('Hello World!');" />。不過 <script>元素提供了更多的靈活性,比如可以通過屬性 args來聲明參數(shù),屬性 with來使用 JavaScript 中的 with表達式。在 <script>元素內部編寫方法體也更加清晰,可以使用代碼縮進。代碼清單 3 給出了一個使用 <script type="dojo/method" >的示例。

 


清單 3. <script type="dojo/method" > 使用示例
 <div dojoType="emailDisplayer" jsId="myEmailDisplayer">
            <script type="dojo/method" event="setEmailByName" args="name">
            var email = name + "@example.org";
            this.attr("email", email);
            </script>
            </div>

代碼清單 3 中使用了 代碼清單 1 中給出的顯示電子郵件地址的 Dijit 組件,并通過 <script type="dojo/method" >定義了一個新的 JavaScript 方法 setEmailByName。通過屬性 jsId可以為組件設置一個全局的引用名稱。當組件實例化之后,就可以通過 myEmailDisplayer.setEmailByName("Alex")來調用方法 setEmailByName(),組件會顯示出 Alex@example.org。

在聲明了 Dijit 組件之后,需要通過 dojo.parser.parse(rootNode, args)方法來完成組件的實例化工作。該方法可以遍歷 rootNode的子孫節(jié)點,根據(jù)是否包含屬性 dojoType來判斷是否為 Dijit 組件。如果是的話,就實例化該組件。該方法會返回一個實例化出來的組件列表。在實例化一個 Dijit 組件的時候,默認的方式是通過 new操作符來完成的。dojo.parser.parse()方法也允許開發(fā)人員自定義組件實例化的行為。通過在組件類或是其 prototype對象上定義 markupFactory方法就可以完成。dojo.parser.parse()方法會優(yōu)先檢查是否存在 markupFactory方法。如果有的話,就把此方法的返回值作為實例化出來的組件對象。

編程式

與聲明式的方式相比,編程式的方式相對簡單。只需要通過 new操作符,并傳入合適的參數(shù)即可。如 new dijit.form.Button({label : "Press Me!"})就創(chuàng)建了一個新的 dijit.form.Button組件。

兩種方式的比較

聲明式和編程式兩種方式是密切相關的。只不過使用聲明式的情況下,是由 Dojo 庫在后臺以編程的方式來實現(xiàn) Dijit 組件的實例化的。兩種方式從使用上來說,聲明式方式比較易懂,開發(fā)人員不需要了解太多 JavaScript 語言的細節(jié),就可以用直觀的方式來定義出所需要創(chuàng)建的組件;編程式的方式則功能更加強大,給了開發(fā)人員更多的靈活性。如果創(chuàng)建組件時所需要的參數(shù)是在運行時刻動態(tài)計算出來的話,使用聲明式就無法實現(xiàn),只能以編程的方式來創(chuàng)建。

一般來說,對于簡單的和容器類的組件,最好使用聲明式的方式來創(chuàng)建。簡單的組件用聲明式的方式非常簡潔;而容器類的組件,即混入了 dijit._Container的組件,如 dijit.layout.TabContainerdijit.layout.BorderContainer,使用聲明式的方式可以很直觀的定義容器的子組件,而使用編程式的方式則需要通過 addChild()來逐個添加子組件,過程比較繁瑣。如果創(chuàng)建組件時可以允許的屬性比較復雜,或是屬性的值需要動態(tài)計算,則最好使用編程式的方式來創(chuàng)建。開發(fā)人員可以根據(jù)實際情況來選擇最適合自己的組件創(chuàng)建方式。


總結

Dojo 默認提供的 Dijit 組件庫中包含的都是一些通用的組件。在 Ajax 應用開發(fā)中,經(jīng)常會需要根據(jù)業(yè)務邏輯的需要開發(fā)出自己的組件。在開發(fā)自己組件的時候,深入了解 Dijit 組件的編程模型和相關的最佳實踐是大有益處的。本文介紹了 Dijit 組件編程模型中的核心類 dijit._Widget、dijit._Templateddijit._Container,以及管理和實例化 Dijit 組件相關的內容。在介紹的過程中,穿插了對最佳實踐的說明,可以幫助開發(fā)人員更好的開發(fā)自己的 Dijit 組件。


聲明

本人所發(fā)表的內容僅為個人觀點,不代表 IBM 公司立場、戰(zhàn)略和觀點。


參考資料

學習

討論

    本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內容均由用戶發(fā)布,不代表本站觀點。請注意甄別內容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權內容,請點擊一鍵舉報。
    轉藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    中文字幕乱子论一区二区三区| 国产又黄又爽又粗视频在线| 欧美中文日韩一区久久| 麻豆蜜桃星空传媒在线观看| 精品日韩中文字幕视频在线| 欧洲精品一区二区三区四区| 亚洲天堂精品1024| 十八禁日本一区二区三区| 日韩一区二区三区在线欧洲| 中日韩美女黄色一级片| 黑丝袜美女老师的小逼逼| 日韩欧美三级中文字幕| 国产精品久久精品国产| 丰满人妻一二区二区三区av| 青青操视频在线播放免费| 中文字幕一区二区熟女| 成人精品日韩专区在线观看 | 91精品国产综合久久精品| 精品日韩国产高清毛片| 九九九热视频免费观看| 午夜精品在线观看视频午夜| 国产精品午夜视频免费观看 | 欧美日韩黑人免费观看| 中文日韩精品视频在线| 国产对白老熟女正在播放| 亚洲一区精品二人人爽久久| 一区二区三区免费公开| 国产午夜精品美女露脸视频| 国产午夜精品美女露脸视频| 午夜久久精品福利视频| 亚洲最大福利在线观看| 中文字幕乱码一区二区三区四区| 日韩一级一片内射视频4k| 国产亚洲系列91精品| 99久久免费看国产精品| 中文字幕亚洲在线一区| 91天堂免费在线观看| 青青久久亚洲婷婷中文网| 国产精品亚洲欧美一区麻豆| 噜噜中文字幕一区二区| 国产成人精品综合久久久看|