Dijit 組件(widget)是 Dojo 提供的圖形用戶界面組件庫。它提供了 Ajax 應用開發(fā)中會用到的常用組件,可以幫助開發(fā)人員快速的構建 Ajax 應用。本文并不會介紹 Dojo 默認提供的組件,而是側重于介紹 Dijit 組件的編程模型和最佳實踐,其目的是幫助開發(fā)人員更好的開發(fā)自己的 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 組件的時候,需要注意下面幾個基本的問題。
dojo.connect()
來綁定到該方法上。組件使用該方法來通知使用者其內(nèi)部狀態(tài)的變化。這類方法一般以 on
作為名稱前綴。開發(fā)人員應該根據(jù)需要定義合適的接口,避免一些不好的實踐。比如公共屬性的值在組件創(chuàng)建之后,一般不推薦使用者設置其值。如果設置該屬性的值是一個合理的場景的話,最好提供相應的公共方法,并以文檔的形式告訴使用者正確的用法。dojo.publish()
和 dojo.subscribe()
方法來完成。這種做法的使用比較簡單,可以避免層次較深的對象引用傳遞。不好的地方是組件之間的關聯(lián)關系不夠清晰,也比較難維護。推薦的做法是優(yōu)先使用第一種方式。
上面對開發(fā) Dijit 組件的一些通用問題進行了討論。下面開始介紹 Dijit 組件的編程模型。在編程模型的介紹過程中,會穿插介紹相關的最佳實踐。首先從 Dijit 組件的核心類 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)建時的生命周期的默認模板。該方法會在合適的時機調(diào)用模板中包含的其它方法。這些不同的方法構成了組件生命周期中的各個階段。開發(fā)自己的組件的時候,可以覆寫其中的某些方法,從而在感興趣的階段添加自己的處理邏輯。開發(fā)人員也可以覆寫 create()
方法來提供一套完全不同的生命周期實現(xiàn)。不過這種做法風險太大,不建議使用。絕大多數(shù)情況下,覆寫默認模板提供的方法就足夠了。dijit._Widget
中定義的組件生命周期中的創(chuàng)建階段如 圖 1 所示。
如 圖 1 所示,創(chuàng)建 Dijit 組件時的過程中包含如下幾個步驟:
postMixInProperties()
方法被調(diào)用。buildRendering()
方法被調(diào)用。postCreate()
方法被調(diào)用。startup()
方法。
圖 1 中給出了 4 個方法的名稱,其中橢圓形中的 postMixInProperties()
、buildRendering()
和 postCreate()
是 dijit._Widget
提供的組件生命周期中的擴展點。自己開發(fā)的組件應該通過覆寫這些方法來實現(xiàn)自己的邏輯。圓角矩形中的 startup()
方法并不是創(chuàng)建過程中的一部分,需要在 Dijit 組件創(chuàng)建完成之后顯式的調(diào)用。下面對這 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)建完成之后,此方法被調(diào)用。需要注意的是,這個時候組件的 DOM 節(jié)點可能還沒有被添加到當前頁面文檔樹中。startup()
:當 Dijit 組件及其子組件被創(chuàng)建完成并添加到當前頁面文檔樹中之后,顯式的調(diào)用此方法。該方法對于那些包含子組件的 Dijit 組件來說非常有用,可以用來控制子組件和進行布局。startup()
方法只需要調(diào)用一次即可。調(diào)用完成之后,組件的屬性 _started
的值為 true
,可以用來判斷 startup()
方法是否已經(jīng)被調(diào)用過。
與創(chuàng)建 Dijit 組件對應的組件的銷毀過程。銷毀過程比較復雜,涉及到 5 個方法,如 圖 2 所示。圖 2 中的箭頭表示的是方法之間的調(diào)用關系。
如 圖 2 所示,最常用的銷毀一個 Dijit 組件的方法是 destroyRecursive()
。該方法用來銷毀一個 Dijit 組件及其包含的子組件。該方法會首先調(diào)用 destroyDescendants()
方法來銷毀子組件。由于子組件也可能包含自己的子組件,destroyDescendants()
也會調(diào)用 destroyRecursive()
方法來刪除其子組件,從而形成一個遞歸的銷毀過程。當子組件銷毀完成之后,destroyRecursive()
會調(diào)用 destroy()
方法來銷毀自己。destroy()
方法會首先調(diào)用 uninitialize()
,接著執(zhí)行內(nèi)部的清理工作,最后調(diào)用 destroyRendering()
方法來銷毀組件的 DOM 節(jié)點。需要注意的是,destroy()
方法會銷毀在 Dijit 組件模板中定義的子 Dijit 組件。這 5 個方法中除了 uninitialize()
之外的其它 4 個方法,都有一個參數(shù) preserveDom
用來表明是否保留 Dijit 組件的 DOM 節(jié)點,不過對從模板創(chuàng)建出的 Dijit 組件無效。
在深入理解了 Dijit 組件的生命周期之后,就可以更好的利用 Dijit 庫提供的支持來開發(fā)自己 Dijit 組件。下面介紹一些與組件生命周期相關的最佳實踐。
postCreate()
方法中添加組件相關的處理邏輯即可。如果需要添加與 DOM 節(jié)點大小和位置相關的邏輯,應該放在 startup()
方法中,并要求組件的使用者顯式調(diào)用。在覆寫方法的時候,要通過 this.inherited(arguments);
來調(diào)用 dijit._Widget
類的原始方法。uninitialize()
方法中,并且只包含當前組件相關的邏輯,不需要考慮子組件。destroyRecursive()
方法會負責處理子組件的銷毀。destroyRecursive()
來銷毀一個 Dijit 組件。這樣可以確保子組件總是被正常銷毀,避免內(nèi)存泄露。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 中給出了一個獲取和設置屬性的示例。
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
定義了兩個屬性 email
和 replaceString
。對于屬性 email
提供了自定義的設置方法 _setEmailAttr()
。對于屬性 replaceString
則使用的是默認的實現(xiàn)。
對于統(tǒng)一的 attr()
方法接口和自定義的屬性獲取和設置方法,Dijit 組件既可以方便使用者的使用,又給了開發(fā)人員足夠的靈活性。在組件開發(fā)中,盡量避免提供 Dijit 組件自己的屬性獲取和設置方法,而是充分利用 Dijit 組件提供的支持。
除了上面提到的兩類方法之外,dijit._Widget
還提供了其它一些方法。
connect()
方法來綁定事件處理方法。其好處是在 Dijit 組件被銷毀的時候,會自動調(diào)用 disconnect()
來取消事件綁定。subscribe()
方法來監(jiān)聽事件通知。其好處是在 Dijit 組件被銷毀的時候,會自動調(diào)用 unsubscribe()
來取消事件監(jiān)聽。
在介紹完 dijit._Widget
之后,下面介紹另外一個核心類 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)錯誤。templateString
和 templatePath
兩種方式來指定所使用的模板。從 Dojo 1.4 開始,建議使用 templateString
來指定模板字符串。不過在 Dijit 組件開發(fā)過程中,把模板存放在單獨的 HTML 文件中更加便于開發(fā)和調(diào)試。因此在開發(fā)過程中可以使用 templatePath
。在發(fā)布的時候,則需要通過 Dojo 的構建過程把 HTML 文件的內(nèi)容內(nèi)聯(lián)到組件代碼中,通過 templateString
來表示。widgetsInTemplate
的值為 true
可以聲明該 Dijit 組件中包含其它的組件。這些包含的組件在 destroy()
方法中會被銷毀。
在模板中可以使用 dojoAttachPoint
和 dojoAttachEvent
兩個特殊的 DOM 節(jié)點的屬性。dojoAttachPoint
屬性的值被轉換成組件對象中的一個屬性的名稱,該屬性的值是當前的 DOM 節(jié)點。dojoAttachEvent
屬性的值被轉換成通過 dojo.connect()
完成的事件處理綁定。如 <div dojoAttachPoint="myDiv" dojoAttachEvent="onclick:show" />
聲明了該 DOM 節(jié)點可以在組件對象中通過 myDiv
來引用,點擊該節(jié)點會調(diào)用 show()
方法。這兩個屬性的存在帶來了編程上的簡便。在組件對象的方法中,如果需要引用某個 DOM 節(jié)點的話,一般需要通過 DOM 查詢或是 dojo.query()
來完成,這樣的話會比較繁瑣。通過 dojoAttachPoint
就避免了查詢操作,使用起來更加簡單。如果需要綁定事件處理的話,使用 dojoAttachEvent
就免去了對 dojo.connect()
方法的顯式調(diào)用。不過使用這兩個屬性的話,會造成變量的聲明和使用在不同的地方,會在一定程度上影響代碼的可讀性。比較好的實踐如下:
dojoAttachPoint
聲明的屬性和一般的屬性,最好為 dojoAttachPoint
聲明的屬性名稱添加統(tǒng)一的后綴,如 Node
或是 Container
。其好處是開發(fā)人員在發(fā)現(xiàn)帶某個后綴的屬性時,會明白要去模板中查找相關的聲明。dojoAttachPoint
和 dojoAttachEvent
,也可以統(tǒng)一使用 DOM 查詢和 dojo.connect()
。最好不要兩種方式混用。項目開發(fā)團隊應該根據(jù)團隊的意見,制定出相關的代碼編寫規(guī)范。
在介紹完 dijit._Templated
之后,下面介紹另外一個核心類 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()
方法會負責調(diào)用子組件的 startup()
方法。對于通過 addChild()
方法動態(tài)添加的子組件,如果子組件的 startup()
方法沒有被調(diào)用過,則會調(diào)用此 startup()
方法。removeChild()
只是將子組件移除,使其不再受 dijit._Container
的管理,并不會銷毀該子組件。
在介紹完 dijit._Container
之后,下面介紹如何對 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.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)
來調(diào)用父類的邏輯的話,就很容易出現(xiàn)這樣的錯誤。
在介紹完 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ù)類型的,因此需要一個轉換的過程。了解此過程有助解決一些屬性無法設置的問題。這個轉換過程具體如下:
dojoType
聲明的組件類,遍歷該類的 prototype
對象中的屬性,去掉以“_”開頭的和 Object.prototype
中包含的屬性,其余的就是可以在聲明 Dijit 組件的時候使用的屬性。這些屬性的值的數(shù)據(jù)類型也會被記錄下來。
dojo.declare("myWidget", dijit._Widget, { count : 1, valid : false, names : ["Alex", "Bob"] }); <div dojoType="myWidget" count="20" names="John, Jason" /> |
在 代碼清單 2 給出的例子中,myWidget
中定義了 3 個屬性:count
、valid
和 names
,其數(shù)據(jù)類型分別是數(shù)字、布爾型和數(shù)組。在聲明 Dijit 組件的時候,在 DOM 節(jié)點上添加了屬性 count
和 names
。屬性 count
的值被轉換成數(shù)字,而屬性 names
的值被轉換成數(shù)組。
除了通過 DOM 節(jié)點的屬性來設置 Dijit 組件的屬性之外,還可以通過 <script>
子元素來聲明方法。<script>
元素的內(nèi)容就是方法體本身。支持的聲明方式有三種:
<script type="dojo/method" event="myHandler">
:聲明的 JavaScript 方法與其它簡單屬性一樣,被混入到組件對象中。屬性名稱是 event
的值。<script type="dojo/method" >
:聲明的 JavaScript 方法在組件實例化之后會被立即執(zhí)行。<script type="dojo/connect" event="onClick" >
:聲明的 JavaScript 方法在組件實例化之后會通過 dojo.connect()
綁定到 event
所指明的事件上。<div dojoType="myWidget" myHandler="alert('Hello World!');" />
。不過 <script>
元素提供了更多的靈活性,比如可以通過屬性 args
來聲明參數(shù),屬性 with
來使用 JavaScript 中的 with
表達式。在 <script>
元素內(nèi)部編寫方法體也更加清晰,可以使用代碼縮進。代碼清單 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")
來調(diào)用方法 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.TabContainer
和 dijit.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._Templated
和 dijit._Container
,以及管理和實例化 Dijit 組件相關的內(nèi)容。在介紹的過程中,穿插了對最佳實踐的說明,可以幫助開發(fā)人員更好的開發(fā)自己的 Dijit 組件。
本人所發(fā)表的內(nèi)容僅為個人觀點,不代表 IBM 公司立場、戰(zhàn)略和觀點。
學習
討論