ExtJS 4 的應用界面是由很多小部件組合而成的,這些小部件被稱作“組件(Component
)”,所有組件都是Ext.Component
的子類,Ext.Component
提供了生命周期管理包括初始化、渲染、大小和尺寸管理、銷毀等功能,這使得所有Ext.Component
的子類都自動分享了這些能力。ExtJS提供了各式各樣豐富的組件,每一個組件都很容易被擴展創(chuàng)建成自定義組件。
The Component Hierarchy 組件層級
容器(Container
)是個可以容納其他組件的特殊組件。通常的應用都是由很多嵌套的組件構(gòu)成,這些組件有一個類似樹狀的結(jié)構(gòu),這就是組件的層級。容器負責管理子組件的生命周期包括始化、渲染、大小和尺寸管理、銷毀。通常的應用最頂層的組件都是Viewport
然后有其他容器或者組件嵌套在其中:
子組件通過容器的items
加入到容器中,下面這個例子通過Ext.create
方法創(chuàng)建了兩個Panel
,并把它們當作子組件添加到Viewport
中:
1
2
3
4
5
6
7
8
9
10
11
12
13
var childPanel1 = Ext.create('Ext.panel.Panel', {
title: 'Child Panel 1',
html: 'A Panel'
});
var childPanel2 = Ext.create('Ext.panel.Panel', {
title: 'Child Panel 2',
html: 'Another Panel'
});
Ext.create('Ext.container.Viewport', {
items: [ childPanel1, childPanel2 ]
});
容器通過“布局管理器(Layout Manager
)”管理子組件的尺寸和位置。關(guān)于布局和容器的詳細內(nèi)容可以查看《ExtJS 4 布局和容器 》
XTypes and Lazy Instantiation xtype和延遲加載
每個組件都有一個代稱叫做xtype
,例如Ext.panel.Panel
的xtype
是panel
。所有組件的xtype
在API Docs for Component 中有列出。上面那個例子說明了如何添加已經(jīng)初始化的組件到一個容器中。在大的應用中,這種方式是不可行的,因為這種方法需要每個組件都是初始化過的,但是大的應用中,由于使用的方式有些組件可能根本沒有被用到(比如有100個頁面的應用,一個用戶登錄進來只操作了兩個頁面,這時把其他98個頁面的組件全部初始化是不合理的),例如一個用到TabPanel
的應用,TabPanel
的每個tab只當用戶點擊它的時候再渲染即可。這就是引入xtype
機制的原因,有了xtype
可以讓一個容器的子組件事先定義好,到真正需要的時候再初始化。
下面的示例代碼通過TabPanel
展示了延遲加載和延遲渲染,每個tab都有個響應函數(shù)監(jiān)聽tab的渲染render
事件,渲染的時候會彈出一個對話框告知當前tab已經(jīng)渲染
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Ext.create('Ext.tab.Panel', {
renderTo: Ext.getBody(),
height: 100,
width: 200,
items: [
{
// Explicitly define the xtype of this Component configuration.
// This tells the Container (the tab panel in this case)
// to instantiate a Ext.panel.Panel when it deems necessary
xtype: 'panel',
title: 'Tab One',
html: 'The first tab',
listeners: {
render: function() {
Ext.MessageBox.alert('Rendered One', 'Tab One was rendered.');
}
}
},
{
// this component configuration does not have an xtype since 'panel' is the default
// xtype for all Component configurations in a Container
title: 'Tab Two',
html: 'The second tab',
listeners: {
render: function() {
Ext.MessageBox.alert('Rendered One', 'Tab Two was rendered.');
}
}
}
]
});
運行一下代碼,第一個tab的渲染對話框馬上就彈出了,因為默認激活第一個tab,所以它的父容器TabPanel
立即初始化和渲染了它
不點擊第二個tab,它的對話跨就一直不會彈出,這說明tab不會被渲染直到被用到,tab的render
事件不會觸發(fā)直到tab被激活
Showing and Hiding 顯示和隱藏
所有組件都有內(nèi)置的show
和hide
方法。默認的css樣式應用的是display: none
,可以通過hideMode
改變
1
2
3
4
5
6
7
8
9
10
var panel = Ext.create('Ext.panel.Panel', {
renderTo: Ext.getBody(),
title: 'Test',
html: 'Test Panel',
hideMode: 'visibility' // use the CSS visibility property to show and hide this component
});
panel.hide(); // hide the component
panel.show(); // show the component
Floating Components 浮動組件
浮動組件定位于文檔流之外,使用的是CSS的絕對定位屬性,不受父容器的布局控制。有些組件,例如Window
,默認就是浮動的,任何組件都可以通過floating
屬性配置成浮動的。
1
2
3
4
5
6
7
var panel = Ext.create('Ext.panel.Panel', {
width: 200,
height: 100,
floating: true, // make this panel an absolutely-positioned floating component
title: 'Test',
html: 'Test Panel'
});
上面的代碼實例化了一個Panel
對象,但是并沒有渲染它。通常一個組件要么有renderTo
屬性配置渲染到什么位置,要么就作為容器的一個子組件由容器負責渲染,但是這個例子的浮動組件即不需要renderTo
也不需要作為子組件。浮動組件第一次調(diào)用show
方法時,會被自動渲染到DOM的document.body中:
1
panel.show(); // render and show the floating panel
有幾個屬性是使用浮動組件時值得注意的:
draggable
- 讓浮動組件可以拖拽
shadow
- 定制浮動組件的陰影效果
alignTo()
- 讓浮動組件對齊到一個特定元素
center()
- 讓浮動組件相對于容器居中
Creating Custom Components 自定義組件
Composition or Extension 組合還是繼承
當創(chuàng)建一個新的UI類時,必須決定這個類是引用其他組件的實例還是擴展這個組件。
推薦擴展最接近的組件添加自己需要的功能,這是因為使用繼承的方式可以自動獲得組件原有的自動生命周期管理能力,包括按須渲染、布局管理器提供的尺寸位置管理、從容器中刪除的自動析構(gòu)等。
擴展一個原有組件會讓擴展出的組件隸屬于組件層級中,這樣比一個外部類引用并掌管ExtJS組件的實例要容易的多。
Subclassing 子類化
ExtJS的類系統(tǒng) 使得擴展一個現(xiàn)有組件變的很簡單。接下來的代碼演示了創(chuàng)建一個Ext.Component
的子類,不添加任何功能:
1
2
3
Ext.define('My.custom.Component', {
extend: 'Ext.Component'
});
Template Methods 模板方法
ExtJS使用了模板方法模式 把特殊行為委托給子類。
這意味著每一個繼承鏈上的類都可以“貢獻”一段包含特定邏輯的片段到組件的生命周期中。每一個類實現(xiàn)自己特殊行為的同時,還允許其他在繼承鏈上的類繼續(xù)貢獻它們自己的邏輯。
一個例子就是render
方法。render
是個私有方法,是在Component
的父類AbstractComponent
中定義的,render
在組件生命周期中負責渲染階段的初始化。render
不能被重寫,但是render
過程中會調(diào)用onRender
,這個onRender
方法是允許子類去實現(xiàn)的,子類可以在這里增加專屬于子類的邏輯,每一個onRender
方法必須在自己的額外邏輯之前調(diào)用父類的onRender
。
下圖指示了onRender
模板方法是如何工作的。
render
方法首先被調(diào)用(這是布局管理器完成的),render
方法不能被覆蓋,它是ExtJS中的相關(guān)基類實現(xiàn)的,它會調(diào)用當前子類的this.onRender
(如果當前子類有實現(xiàn)onRender
),這會使得繼續(xù)調(diào)用父類的onRender
,父類的onRender
會繼續(xù)調(diào)用父類的,最后每個繼承鏈上的類都貢獻了自己的功能片段,并且控制回到render
方法中。
這有一個實現(xiàn)onRender
的例子
1
2
3
4
5
6
7
8
Ext.define('My.custom.Component', {
extend: 'Ext.Component',
onRender: function() {
this.callParent(arguments); // call the superclass onRender method
// perform additional rendering tasks here.
}
});
需要注意的是很多模板方法都有一個對應的事件。例如組件render
完成之后會觸發(fā)render
事件。創(chuàng)建子類的時候最好是使用模板方法,而不是使用事件,因為模板方法一定會被執(zhí)行,事件是可以被中斷的(這里是我的補充:什么時候用事件,什么時候用模板方法?模板方法是面向你創(chuàng)建的子類的所有實例的,如果你需要增加的功能片段確實被所有實例需要,那一定要放在模板方法中,如果是某一個實例特殊需要的功能,請用事件實現(xiàn),事件是可以對單獨某個對象實現(xiàn)的)
下面列舉的是Component
的子類中可以實現(xiàn)的模板方法:
initComponent
這個方法被構(gòu)造器調(diào)用,它被用來初始化數(shù)據(jù),設(shè)置配置,添加事件
beforeShow
這個方法在組件顯示前調(diào)用
onShow
允許組件顯示的時候附加行為,調(diào)用父類的onShow
之后,組件變?yōu)榭梢?/li>
afterShow
這個方法在組件顯示完成后調(diào)用
onShowComplete
這個方法在afterShow
執(zhí)行完畢的時候調(diào)用
onHide
允許組件隱藏的時候附加行為,調(diào)用父類的onHide
之后,組件變?yōu)椴豢梢?/li>
afterHide
這個放在組件隱藏完成后調(diào)用
onRender
允許在渲染階段附加行為
afterRender
允許在渲染完畢時附加行為,在這個階段,組件的Element
已經(jīng)賦予了樣式,該加的css類都已經(jīng)加上,顯示和啟用與否的狀態(tài)都標記完成
onEnable
允許啟用操作時附加行為,調(diào)用父類的onEnable
之后,組件變?yōu)閱⒂?/li>
onDisable
允許禁用操作時附加行為,調(diào)用父類的onDisable
之后,組件變?yōu)榻?/li>
onAdded
允許一個組件被添加到一個容器的時候附加行為,在這個階段,組件已經(jīng)在父容器的items中,調(diào)用父類的onAdded
之后,ownerCt引用被設(shè)置,如果配置了ref,refOwner這時也被設(shè)置
onRemoved
允許一個組件從父容器中刪除的時候附加行為,在這個階段,組件已經(jīng)從父容器的items中移除,但是還沒有被銷毀(如果父容器的autoDestroy設(shè)置為true,或者如果刪除操作remove函數(shù)的第二個參數(shù)設(shè)置為true,組件就會被銷毀),調(diào)用父類的onRemoved
之后,ownerCt和refOwner就會移除
onResize
允許改變大小的時候附加行為
onPosition
允許設(shè)置位置的時候附加行為
onDestroy
允許銷毀時附加行為,調(diào)用父類的onDestroy
之后,組件被銷毀
beforeDestroy
組件銷毀前調(diào)用
afterSetPosition
組件位置設(shè)置之后調(diào)用
afterComponentLayout
組件布局完成時調(diào)用
beforeComponentLayout
組件布局之前調(diào)用
Which Class To Extend 擴展那個類
選擇最合適的類繼承是個影響效率的問題,基類能提供的功能也是個問題?,F(xiàn)在有種傾向是無論什么樣的UI組件都從Ext.Panel
繼承
Panel類有很多能力:
Border
Header
Header tools
Footer
Footer buttons
Top toolbar
Bottom toolbar
Containing and managing child Components
如果不需要這些功能,繼承Ext.Panel
是浪費資源。
Component 組件
如果創(chuàng)建的UI組件不需要包含任何其他組件,就比如只是對HTML片段的封裝就可以滿足需求的組件,推薦擴展Ext.Component
,例如下面的例子封裝了HTML image元素,允許設(shè)置image的src,并且圖片加載完成會觸發(fā)load
事件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
Ext.define('Ext.ux.Image', {
extend: 'Ext.Component', // subclass Ext.Component
alias: 'widget.managedimage', // this component will have an xtype of 'managedimage'
autoEl: {
tag: 'img',
src: Ext.BLANK_IMAGE_URL,
cls: 'my-managed-image'
},
// Add custom processing to the onRender phase.
// Add a ‘load’ listener to the element.
onRender: function() {
this.autoEl = Ext.apply({}, this.initialConfig, this.autoEl);
this.callParent(arguments);
this.el.on('load', this.onLoad, this);
},
onLoad: function() {
this.fireEvent('load', this);
},
setSrc: function(src) {
if (this.rendered) {
this.el.dom.src = src;
} else {
this.src = src;
}
},
getSrc: function(src) {
return this.el.dom.src || this.src;
}
});
用法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
var image = Ext.create('Ext.ux.Image');
Ext.create('Ext.panel.Panel', {
title: 'Image Panel',
height: 200,
renderTo: Ext.getBody(),
items: [ image ]
})
image.on('load', function() {
console.log('image loaded: ', image.getSrc());
});
image.setSrc('http://www./img/sencha-large.png');
這個例子僅作為例子,Ext.Img
可以在真實應用中使用。
Container 容器
如果UI組件需要包含其他組件的能力,但是又不需要Panel
那么多功能Ext.container.Container
是個很好的選擇。使用它有一點需要注意,記得使用Layout
管理子組件。
Ext.container.Container
有如下模板方法:
onBeforeAdd
這個方法在添加一個子組件之前調(diào)用,方法會被傳入添加進來的子組件,你可能會對子組件進行一些修改,或者對容器自身做一些準備工作,返回false會中斷添加操作。
onAdd
這個方法在新組件被添加完成時調(diào)用,方法會傳入新添加的組件,這個方法可以用來更新依賴子組件狀態(tài)的內(nèi)部結(jié)構(gòu)
onRemove
子組件被刪除之后調(diào)用,用處跟onAdd
類似
beforeLayout
組件布局它的子組件之前調(diào)用的方法
afterLayout
組件布局它的子組件之后調(diào)用的方法
Panel 面板
如果需求的UI組件必須有header,footer或者toolbar,Ext.panel.Panel
比較適合
注意Panel
也是個容器,也需要Layout
管理子組件
從Panel
擴展出的組件一般都是跟具體應用相關(guān)的,通常都聚合了其他組件在其中,一般都提供了操作內(nèi)部組件的方法,比如在toolbar上有操作內(nèi)部組件的按鈕等
Panel
有如下附加的模板方法:
afterCollapse
組件折疊起來之后調(diào)用
afterExpand
組件展開之后調(diào)用,與afterCollapse相對應
onDockedAdd
工具條上有子組件添加之后調(diào)用
onDockedRemove
工具條上有子組件被移除后調(diào)用
更多教程