Menu

  • Home
  • Work
    • Cloud
      • Virtualization
      • IaaS
      • PaaS
    • Java
    • Go
    • C
    • C++
    • JavaScript
    • PHP
    • Python
    • Architecture
    • Others
      • Assembly
      • Ruby
      • Perl
      • Lua
      • Rust
      • XML
      • Network
      • IoT
      • GIS
      • Algorithm
      • AI
      • Math
      • RE
      • Graphic
    • OS
      • Linux
      • Windows
      • Mac OS X
    • BigData
    • Database
      • MySQL
      • Oracle
    • Mobile
      • Android
      • IOS
    • Web
      • HTML
      • CSS
  • Life
    • Cooking
    • Travel
    • Gardening
  • Gallery
  • Video
  • Music
  • Essay
  • Home
  • Work
    • Cloud
      • Virtualization
      • IaaS
      • PaaS
    • Java
    • Go
    • C
    • C++
    • JavaScript
    • PHP
    • Python
    • Architecture
    • Others
      • Assembly
      • Ruby
      • Perl
      • Lua
      • Rust
      • XML
      • Network
      • IoT
      • GIS
      • Algorithm
      • AI
      • Math
      • RE
      • Graphic
    • OS
      • Linux
      • Windows
      • Mac OS X
    • BigData
    • Database
      • MySQL
      • Oracle
    • Mobile
      • Android
      • IOS
    • Web
      • HTML
      • CSS
  • Life
    • Cooking
    • Travel
    • Gardening
  • Gallery
  • Video
  • Music
  • Essay

ExtJS 4的组件机制

5
Apr
2013

ExtJS 4的组件机制

By Alex
/ in JavaScript
/ tags ExtJS
0 Comments
ExtJS组件基础类

组件、容器是ExtJS组件机制的基础,后者是前者的子类型。

Ext.util.Renderable

这是一个ExtJS框架的私有混入,定义了组件渲染的核心逻辑,对于下面这个包含三层嵌套的Panel:

JavaScript
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
Ext.onReady( function()
{
    Ext.createByAlias( 'widget.panel', {
        id : 'ContainerA',
        layout : 'hbox',
        width : 500,
        height : 500,
        renderTo : Ext.getBody(),
        items : [
            {
                id : 'ContainerB',
                xtype : 'container',
                width : 250,
                height : 250,
                layout : 'fit',
                items : [
                    {
                        id : 'ComponentA',
                        html : 'Hello World !'
                    }
                ]
            }
        ]
    } );
} )

Renderable定义其渲染过程如下:

  1. ContainerA被初始化时转调父类构造器,在AbstractComponent.constructor()中,由于配置了renderTo,调用render()方法,进入渲染阶段
  2. render()开始渲染ContainerA,首先禁用布局系统,然后调用getRenderTree(),获取“渲染树”,这个所谓的渲染树就是一个DomHelper规格对象
  3. getRenderTree()触发事件beforerender、调用beforeRender()模板方法,然后调用getElConfig()获取渲染树对象
  4. 根据是否配置外框(Window、Tip必须有外框),getElConfig()会生成不同的结果:要么调用initFramingTpl(),要么调用initRenderTpl()生成一个模板,并置入渲染树:{tag : 'div', tpl: template}的tpl属性
    1. 从细节上看,initRenderTpl()获取当前类(Container)的renderTpl属性作为tpl,该模板具有以下成员:
       属性  说明
      html 容器的HTML结构,整体形式如下:
      XHTML
      1
      {% this.renderDockedItems(out,values,0); %}

      {%this.renderContainer(out,values);%}
      XHTML
      1
      {% this.renderDockedItems(out,values,1); %}
      initialConfig 传入的初始配置对象
      renderBody

      可供在模板中调用的一系列方法
      这些xxx方法都是在:

      1. Renderable.setupFramingTpl()
      2. AbstractContainer.setupRenderTpl()

      等方法中,与容器、布局类的doXxx进行关联得到的。之所以要和布局类的doXxx关联,是因为布局才知道子组件如何渲染

      renderContainer
      renderContent
      renderDockedItems
      renderItems
      renderPadder
    2. 如果配置了autoEl,替换渲染树的tag属性
    3. 调用initRenderData生成tpl的填充上下文(tplData),并提供一些默认值
    4. 最终返回的渲染树形式如下(符合DomHelper规格约定):
      JavaScript
      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
      {
          tag: "div",
          cls: "",
          style: {
              height: "500px"
              width: "500px"
          },
          id: "ContainerA"
          tpl: {
              html: "..."
              initialConfig: {}
              renderBody: function (out, renderData) {...}
              renderContainer: function (out, renderData) {...}
              renderContent: function (out, renderData) {...}
              renderDockedItems: function (out, renderData, after) {...}
              renderItems: function (out, renderData) {...}
              renderPadder: function (out, renderData) {...}
          },
          tplData : {
              $comp: '当前组件'
              baseCls: "x-panel"
              bodyCls: "x-panel-body-default"
              bodyStyle: ""
              componentCls: "x-panel"
              frame: undefined
              id: "ContainerA"
              ui: "default"
              uiCls: []
          }
      }
  5. render()得到渲染树后,使用DomHelper将其插入到文档中,一旦插入,整个组件(包括所有后代组件)的DOM结构就都生成了,结果如下:extjs-render
  6. 插入DOM完毕后,render()设置ContainerA的el为新生成的HTML元素ContainerA
  7. render()调用finishRender()完成渲染阶段,在此阶段,会自上而下的递归调用所有子代组件的onRender(),然后自下而上的递归完成所有子代组件的afterRender()调用,需要注意的是,子代的onRender()完全处于父代的afterRender()调用的内部
    1. 调用onRender()
      1. 设置renderSelectors为成员变量
      2. 清除正在渲染状态:rendering,设置rendered=true
      3. 记住lastBox={width:500,height:500}
    2. 设置targetEl的overflow样式;设置封装元素的visibilityMode
    3. 如果有存在监听器,则发布render事件
    4. 调用afterRender(),该方法最终会触发布局,主要逻辑位于Rendererable中
      1. 调用finishRenderChildren()完成子代的渲染
        1. 组件布局渲染:调用Renderable.finishRenderChildren()渲染内部结构
          1. 获取组件布局getComponentLayout(),这里是Ext.layout.component.Dock
          2. 调用Dock.finishRender(),该方法会获取dockedItems,并调用Ext.layout.Layout.finishRenderItems()逐个渲染
        2. 容器布局渲染:调用AbstractContainer.finishRenderChildren()渲染子组件
          1. 获取容器布局getLayout(),这里是Ext.layout.container.HBox
          2. 调用HBox.finishRender(),后者转调父类(Ext.layout.container.Container)版本
          3. Container.finishRender()获取渲染目标(targetEl)和渲染子组件(仅一个ContainerB)
          4. 调用Container.finishRenderItems(target, items)完成子组件渲染
          5. 转调Ext.layout.Layout.finishRenderItems(),逻辑类似7.4.1.1
            1. 调用ContainerB的Renderable.finishRender,这一小节是类似于7的递归调用
              1. 子组件ContainerB的el属性被设置,不再为空
              2. 调用ContainerB的onRender模板方法,逻辑类似7.1,lastBox为250
              3. 调用ContainerB.afterRender()
                1. finishRenderChildren()
                2. 完成组件布局渲染(Dock)
                3. 完成容器布局渲染(Fit),继续递归用
                  1. ComponentA.finishRender()
                    1. ComponentA.onRender()
                    2. ComponentA.afterRender()
                      1. ……一直递归到最底层组件……
                4. 由于不是顶层容器,不进行updateLayout()
              4. 如果有存在监听器,则发布afterrender事件
              5. 初始化事件:initEvents()
              6. 如果配置为隐藏,那么调用getEl().hide()
          6. 对于每一个item,调用afterRenderItem(item)
      2. 为targetEl添加CSS类styleHtmlCls、增减样式
      3. 由于当前容器是顶层的,调用AbstractComponent.updateLayout()触发布局
        1. 如果当前组件是隐藏的,调用cancelLayout()取消布局
        2. 否则判断是否布局根,如果不是,什么都不做
        3. 由于ContainerA的布局未被暂停,调用静态函数Ext.AbstractComponent.updateLayout执行实际布局逻辑。这里可以看到,AbstractComponent类对象是全局的布局管理对象,updateLayout会把参数指定的组件放入某个队列,并且进行复杂的布局计算
          1. 如果当前正在运行布局,则把ContainerA放入running的无效队列
          2. 如果当前没有运行布局,则把ContainerA放入pending的无效队列,这里匹配这一条
            1. 如果当前布局系统没有被暂停,则立即刷新布局flushLayouts()
    5. 如果有存在监听器,则发布afterrender事件
    6. 初始化事件,调用AbstractComponenet.initEvents()。initEvent()包含若干在组件渲染完毕后,需要初始化的事件监听器
      1. 如果具有afterRenderEvents,则把这些监听器注册为组件子元素的受管监听器,这些事件属于DOM事件
      2. 为当前组件添加焦点监听器(addFocusListener)
    7. 如果配置为隐藏,那么调用getEl().hide()
  8. render()启用布局系统,转调AbstractComponent.resumeLayouts()静态函数
    1. 刷出布局,调用flushLayouts()
    2. 覆写pendingLayouts的runComplete方法防止死循环
    3. 调用pendingLayouts(类型Ext.layout.Context)的run()方法
      1. 调用Context.flushInvalidates()
        1. 转调Context.invalidate(),使相关的组件、容器布局失效
      2. 调用Context.flush()
      3. 调用Context.runComplete()

Renderable类包含以下成员:

配置项/属性/方法  说明
applyRenderSelectors( ) 根据renderSelectors、childEls的配置,设置组件对其内部元素的引用
doApplyRenderTpl()

void ( Object out, Object values ),由XTemplate调用,在framing结构中插入当前组件的内部结构。

如果使用了framing,一个自动生成的模板代替renderTpl作为getElConfig()的主模板

doAutoRender() 执行自动渲染(autoRender)。浮动组件可能具有自己的ownerCt来管理ZIndex,虽然他们总是被渲染到document.body
ensureAttachedToBody() void ( [Boolean runLayout] )。确保当前组件已经被附加到document.body。如果当前组件被渲染到Ext.getDetachedBody(),该方法可以使之添加到document.body,所有配置的位置被还原
finishRender() void ( Number containerIdx )。访问者模式。自上而下的访问组件树的每一个节点,调用其onRender()方法
getFrameInfo() 根据组件性质、配置获取/计算圆角外框的样式信息
getFrameTpl() void ( Object table ),得到圆角外框的模板
getInsertPosition() HTMLElement ( String/Number/Ext.dom.Element/HTMLElement position ),返回一个可以insertBefore的Element
initFrame() 初始化圆角外框模板
initRenderData() Object (),初始化renderData以供renderTpl使用
initRenderTpl( ) Ext.XTemplate(),初始化renderTpl
render()

void ( [Ext.Element/HTMLElement/String container], [String/Number position] ),把组件渲染到给定的HTML元素

如果当前组件被容器管理,不需要调用该方法,因为容器的子组件是在容器第一次布局时,由容器的布局管理器负责渲染的。如果添加一个新子组件时,容器已经渲染,可能需要调用容器的doLayout()来强迫新的、未渲染的组件进行渲染。

当创建复杂的组件UI结构时,需要注意子组件的大小、位置是由父容器的布局管理器维护的。

如果父容器不提供layout配置,那么缺省的布局管理器被使用,这种情况下,只是依次渲染子组件,不会管理其大小和位置。

setupFramingTpl() void ( Object frameTpl ),创建framing模板,该模板包裹renderTpl
  模板方法  
afterRender()

在渲染完成后执行额外逻辑,在此时组件的元素已经生成,并且应用了配置的样式,包括CSS、可见性等

onRender()

void( Ext.core.Element parentNode, Number containerIdx ),当组件的DOM结构被创建后,调用该方法。在此刻,组件(及其所有后代)的DOM结构已经存在,但是尚未被布局(尺寸/位置)。子类必须先调用父类的onRender版本,才能访问DOM结构。

parentNode:当前组件封装元素的元素;containerIdx:当前组件在父容器的索引

Ext.util.Renderable的代码分析如下:

Ext.util.Renderable.js
JavaScript
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
Ext.define('Ext.util.Renderable', {
    requires: [
        'Ext.dom.Element'
    ],
    //外框:在组件外围制造圆角矩形效果
    frameCls: Ext.baseCSSPrefix + 'frame',
    frameIdRegex: /[\-]frame\d+[TMB][LCR]$/,
    frameElementCls: {
        tl: [],
        tc: [],
        tr: [],
        ml: [],
        mc: [],
        mr: [],
        bl: [],
        bc: [],
        br: []
    },
    frameElNames: ['TL','TC','TR','ML','MC','MR','BL','BC','BR'],
    //基于DIV的外框模板
    frameTpl: [
        '{%this.renderDockedItems(out,values,0);%}',//渲染停靠的子组件
        '<tpl if="top">',
            '<tpl if="left"><div id="{fgid}TL" class="{frameCls}-tl {baseCls}-tl {baseCls}-{ui}-tl<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-tl</tpl>" style="background-position: {tl}; padding-left: {frameWidth}px" role="presentation"></tpl>',
                '<tpl if="right"><div id="{fgid}TR" class="{frameCls}-tr {baseCls}-tr {baseCls}-{ui}-tr<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-tr</tpl>" style="background-position: {tr}; padding-right: {frameWidth}px" role="presentation"></tpl>',
                    '<div id="{fgid}TC" class="{frameCls}-tc {baseCls}-tc {baseCls}-{ui}-tc<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-tc</tpl>" style="background-position: {tc}; height: {frameWidth}px" role="presentation"></div>',
                '<tpl if="right"></div></tpl>',
            '<tpl if="left"></div></tpl>',
        '</tpl>',
        '<tpl if="left"><div id="{fgid}ML" class="{frameCls}-ml {baseCls}-ml {baseCls}-{ui}-ml<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-ml</tpl>" style="background-position: {ml}; padding-left: {frameWidth}px" role="presentation"></tpl>',
            '<tpl if="right"><div id="{fgid}MR" class="{frameCls}-mr {baseCls}-mr {baseCls}-{ui}-mr<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-mr</tpl>" style="background-position: {mr}; padding-right: {frameWidth}px" role="presentation"></tpl>',
                '<div id="{fgid}MC" class="{frameCls}-mc {baseCls}-mc {baseCls}-{ui}-mc<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-mc</tpl>" role="presentation">',
                    '{%this.applyRenderTpl(out, values)%}', //渲染主体内容
                '</div>',
            '<tpl if="right"></div></tpl>',
        '<tpl if="left"></div></tpl>',
        '<tpl if="bottom">',
            '<tpl if="left"><div id="{fgid}BL" class="{frameCls}-bl {baseCls}-bl {baseCls}-{ui}-bl<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-bl</tpl>" style="background-position: {bl}; padding-left: {frameWidth}px" role="presentation"></tpl>',
                '<tpl if="right"><div id="{fgid}BR" class="{frameCls}-br {baseCls}-br {baseCls}-{ui}-br<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-br</tpl>" style="background-position: {br}; padding-right: {frameWidth}px" role="presentation"></tpl>',
                    '<div id="{fgid}BC" class="{frameCls}-bc {baseCls}-bc {baseCls}-{ui}-bc<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-bc</tpl>" style="background-position: {bc}; height: {frameWidth}px" role="presentation"></div>',
                '<tpl if="right"></div></tpl>',
            '<tpl if="left"></div></tpl>',
        '</tpl>',
        '{%this.renderDockedItems(out,values,1);%}'//渲染停靠的子组件
    ],
    //基于TABLE的外框模板
    frameTableTpl: [
        '{%this.renderDockedItems(out,values,0);%}',
        '<table><tbody>',
            '<tpl if="top">',
                '<tr>',
                    '<tpl if="left"><td id="{fgid}TL" class="{frameCls}-tl {baseCls}-tl {baseCls}-{ui}-tl<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-tl</tpl>" style="background-position: {tl}; padding-left:{frameWidth}px" role="presentation"></td></tpl>',
                    '<td id="{fgid}TC" class="{frameCls}-tc {baseCls}-tc {baseCls}-{ui}-tc<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-tc</tpl>" style="background-position: {tc}; height: {frameWidth}px" role="presentation"></td>',
                    '<tpl if="right"><td id="{fgid}TR" class="{frameCls}-tr {baseCls}-tr {baseCls}-{ui}-tr<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-tr</tpl>" style="background-position: {tr}; padding-left: {frameWidth}px" role="presentation"></td></tpl>',
                '</tr>',
            '</tpl>',
            '<tr>',
                '<tpl if="left"><td id="{fgid}ML" class="{frameCls}-ml {baseCls}-ml {baseCls}-{ui}-ml<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-ml</tpl>" style="background-position: {ml}; padding-left: {frameWidth}px" role="presentation"></td></tpl>',
                '<td id="{fgid}MC" class="{frameCls}-mc {baseCls}-mc {baseCls}-{ui}-mc<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-mc</tpl>" style="background-position: 0 0;" role="presentation">',
                    '{%this.applyRenderTpl(out, values)%}',
                '</td>',
                '<tpl if="right"><td id="{fgid}MR" class="{frameCls}-mr {baseCls}-mr {baseCls}-{ui}-mr<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-mr</tpl>" style="background-position: {mr}; padding-left: {frameWidth}px" role="presentation"></td></tpl>',
            '</tr>',
            '<tpl if="bottom">',
                '<tr>',
                    '<tpl if="left"><td id="{fgid}BL" class="{frameCls}-bl {baseCls}-bl {baseCls}-{ui}-bl<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-bl</tpl>" style="background-position: {bl}; padding-left: {frameWidth}px" role="presentation"></td></tpl>',
                    '<td id="{fgid}BC" class="{frameCls}-bc {baseCls}-bc {baseCls}-{ui}-bc<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-bc</tpl>" style="background-position: {bc}; height: {frameWidth}px" role="presentation"></td>',
                    '<tpl if="right"><td id="{fgid}BR" class="{frameCls}-br {baseCls}-br {baseCls}-{ui}-br<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-br</tpl>" style="background-position: {br}; padding-left: {frameWidth}px" role="presentation"></td></tpl>',
                '</tr>',
            '</tpl>',
        '</tbody></table>',
        '{%this.renderDockedItems(out,values,1);%}'
    ],
    //执行渲染,生成组件的全部HTML并插入到DOM
    //container是渲染到的HTML父节点
    render: function(container, position) {
        var me = this,
            el = me.el && (me.el = Ext.get(me.el)),
            vetoed,
            tree,
            nextSibling;
        //暂停所有布局
        Ext.suspendLayouts();
        //确保父节点container的引用已经获取
        container = me.initContainer(container);
        //得到下一个兄弟HTML节点,在其前面插入
        nextSibling = me.getInsertPosition(position);
        //对于顶层组件,渲染开始时el是不存在的
        if (!el) {
            //如果当前组件的封装元素还不存在
            //getRenderTree将会发布beforerender事件、调用beforeRender模板方法
            //然后调用getElConfig()获得当前组件(包括全部子组件)完整的DomHelper规格对象
            tree = me.getRenderTree();
            if (me.ownerLayout && me.ownerLayout.transformItemRenderTree) {
                tree = me.ownerLayout.transformItemRenderTree(tree);
            }
            // 注意:如果beforeRender返回false,则tree不生成
            if (tree) {
                //插入当前组件到文档中
                if (nextSibling) {
                    el = Ext.DomHelper.insertBefore(nextSibling, tree);
                } else {
                    el = Ext.DomHelper.append(container, tree);
                }
                //设置this.el变量
                me.wrapPrimaryEl(el);
            }
        } else {
            //如果当前组件的封装元素已经存在
            if (!me.hasListeners.beforerender || me.fireEvent('beforerender', me) !== false) {
                me.initStyles(el);
                if (me.allowDomMove !== false) {
                    if (nextSibling) {
                        container.dom.insertBefore(el.dom, nextSibling);
                    } else {
                        container.dom.appendChild(el.dom);
                    }
                }
            } else {
                vetoed = true;//非顶层组件为true
            }
        }
        //如果当前组件是顶层组件,则自上而下的递归整棵组件树(子代组件的finishRender被递归调用)
        //访问每个组件的onRender()方法
        if (el && !vetoed) {
            me.finishRender(position);
        }
        //恢复布局系统,如果组件的parentNode在document.body下,立即刷新所有未决的布局
        Ext.resumeLayouts(!container.isDetachedBody);
    },
    //获取组件的parentNode
    initContainer: function(container) {
        var me = this;
        //如果没有指定container,则自动获取el的parentNode
        if (!container && me.el) {
            container = me.el.dom.parentNode;
            me.allowDomMove = false;
        }
        me.container = container.dom ? container : Ext.get(container);
        return me.container;
    },
    //所谓渲染树就是一个DomHelper规格对象
    getRenderTree: function() {
        var me = this;
        //发布beforerender事件
        if (!me.hasListeners.beforerender || me.fireEvent('beforerender', me) !== false) {
            //如果beforerender事件句柄没有返回false,则调用beforeRender()模板方法
            me.beforeRender();
            //该标记可以让布局管理器的finishRenderItems、afterFinishRenderItems指导哪些items需要处理
            me.rendering = true;
            //如果当前组件的封装元素以及存在,则返回一个代理元素
            if (me.el) {
                //由于这里正在生成一棵“渲染树”,因此使用一个代理元素(proxy el)在生成的目标DOM的精确位置进行占位
                //在finishRender阶段,代理元素会被真实元素替换
                return {
                    tag: 'div',
                    id: (me.$pid = Ext.id())
                };
            }
            //否则,生成一个代表当前组件(包含子全部子组件)的完整的DomHelper规格对象
            return me.getElConfig();
        }
        return null;
    },
    //生成当前组件完整的DomHelper规格
    getElConfig : function() {
        var me = this,
            autoEl = me.autoEl,
            frameInfo = me.getFrameInfo(),
            //规格对象
            config = {
                tag: 'div',//默认标签是DIV
                //如果具有组件外框效果,则使用frameTpl包裹renderTpl,否则直接使用initRenderTpl
                tpl: frameInfo ? me.initFramingTpl(frameInfo.table) : me.initRenderTpl()
            },
            i, frameElNames, len, suffix, frameGenId;
        //初始化样式
        me.initStyles(me.protoEl);
        me.protoEl.writeTo(config);
        me.protoEl.flush();
        //根据autoEl的配置更新config
        if (Ext.isString(autoEl)) {
            config.tag = autoEl;
        } else {
            Ext.apply(config, autoEl);
        }
        config.id = me.id;
        //何时不为true?
        if (config.tpl) {
            if (frameInfo) {
                frameElNames = me.frameElNames;
                len = frameElNames.length;
                frameGenId = me.id + '-frame1';
                me.frameGenId = 1;
                config.tplData = Ext.apply({}, {
                    $comp:      me, //把当前组件作为上下文
                    fgid:       frameGenId,
                    ui:         me.ui,
                    uiCls:      me.uiCls,
                    frameCls:   me.frameCls,
                    baseCls:    me.baseCls,
                    frameWidth: frameInfo.maxWidth,
                    top:        !!frameInfo.top,
                    left:       !!frameInfo.left,
                    right:      !!frameInfo.right,
                    bottom:     !!frameInfo.bottom,
                    renderData: me.initRenderData()
                }, me.getFramePositions(frameInfo));
                //设置外框子元素与组件的关联(作为成员变量)
                for (i = 0; i < len; i++) {
                    suffix = frameElNames[i];
                    me.addChildEls({ name: 'frame' + suffix, id: frameGenId + suffix });
                }
                //对于面板,必须具有frameBody子元素
                me.addChildEls({
                    name: 'frameBody',
                    id: frameGenId + 'MC'
                });
            } else {
                config.tplData = me.initRenderData();
            }
        }
        return config;
    },
    //生命周期模板方法:渲染之后
    afterRender : function() {
        var me = this,
            data = {},
            protoEl = me.protoEl,
            target = me.getTargetEl(),
            item;
        //首先,递归的调用子组件的afterRender()
        me.finishRenderChildren();
        //为目标元素(el或者frameBody)添加样式
        if (me.styleHtmlContent) {
            target.addCls(me.styleHtmlCls);
        }
        //把protoEl的样式信息(包括style和class)写到对象data
        protoEl.writeTo(data);
        //添加、删除样式
        item = data.removed;
        if (item) {
            target.removeCls(item);
        }
        item = data.cls;
        if (item.length) {
            target.addCls(item);
        }
        item = data.style;
        if (data.style) {
            target.setStyle(item);
        }
        //protoEl没有利用价值了
        me.protoEl = null;
        //如果当前是最外层组件或容器,立即更新布局
        if (!me.ownerCt) {
            me.updateLayout();
        }
    },
    //第一次布局后执行
    afterFirstLayout : function(width, height) {
        var me = this,
            hasX = Ext.isDefined(me.x),
            hasY = Ext.isDefined(me.y),
            pos, xy;
        //设置组件的位置
        if (me.floating && (!hasX || !hasY)) {
            if (me.floatParent) {
                pos = me.floatParent.getTargetEl().getViewRegion();
                xy = me.el.getAlignToXY(me.floatParent.getTargetEl(), 'c-c');
                pos.left = xy[0] - pos.left;
                pos.top =  xy[1] - pos.top;
            } else {
                xy = me.el.getAlignToXY(me.container, 'c-c');
                pos = me.container.translatePoints(xy[0], xy[1]);
            }
            me.x = hasX ? me.x : pos.left;
            me.y = hasY ? me.y : pos.top;
            hasX = hasY = true;
        }
        if (hasX || hasY) {
            me.setPosition(me.x, me.y);
        }
        //调用模板方法并发布事件
        me.onBoxReady(width, height);
        if (me.hasListeners.boxready) {
            me.fireEvent('boxready', me, width, height);
        }
    },
    //第一次布局后的模板方法,缺省适配
    onBoxReady: Ext.emptyFn,
    //根据renderSelectors、childEls的配置,设置组件对其内部元素的引用
    applyRenderSelectors: function() {
        var me = this,
            selectors = me.renderSelectors,
            el = me.el,
            dom = el.dom,
            selector;
        me.applyChildEls(el);
        if (selectors) {
            for (selector in selectors) {
                if (selectors.hasOwnProperty(selector) && selectors[selector]) {
                    me[selector] = Ext.get(Ext.DomQuery.selectNode(selectors[selector], dom));
                }
            }
        }
    },
    //生命周期模板方法:渲染之前
    beforeRender: function () {
        var me = this,
            target = me.getTargetEl(),
            layout = me.getComponentLayout();
        //如果当前组件是诸如Window、Tip之类alwaysFramed的组件,那么设置frame为true
        me.frame = me.frame || me.alwaysFramed;
        //初始化组件布局管理器
        if (!layout.initialized) {
            layout.initLayout();
        }
        //设置targetEl的overflow样式,如果目前targetEl尚不存在,则在finishRender后会设置
        if (target) {
            target.setStyle(me.getOverflowStyle());
            me.overflowStyleSet = true;
        }
        //设置主题样式
        me.setUI(me.ui);
        if (me.disabled) {
            //静默的禁用组件
            me.disable(true);
        }
    },
    doApplyRenderTpl: function(out, values) {
        //注意,该方法的this指向frameTpl
        var me = values.$comp,
            tpl;
        if (!me.rendered) {
            //初始化renderTpl
            tpl = me.initRenderTpl();
            //把renderTpl的填充结果添加到输出中
            tpl.applyOut(values.renderData, out);
        }
    },
    //初始化renderTpl
    initRenderTpl: function() {
        var tpl = this.getTpl('renderTpl');//得到renderTpl作为模板
        if (tpl && !tpl.renderContent) {
            this.setupRenderTpl(tpl);
        }
        return tpl;
    },
    //把renderTpl的renderBody、renderContent设值为doRenderContent函数
    setupRenderTpl: function (renderTpl) {
        renderTpl.renderBody = renderTpl.renderContent = this.doRenderContent;
    },
    doRenderContent: function (out, renderData) {
        //注意,该方法的this指向frameTpl
        var me = renderData.$comp;
        if (me.html) {
            //如果指定了html配置,把它输出到out
            Ext.DomHelper.generateMarkup(me.html, out);
            delete me.html;//删除配置
        }
        if (me.tpl) {
            //如果指定了tpl,将其填充结果输出到out
            if (!me.tpl.isTemplate) {
                me.tpl = new Ext.XTemplate(me.tpl);
            }
            if (me.data) {
                me.tpl.applyOut(me.data, out);
                delete me.data;
            }
        }
    },
    //执行自动渲染
    doAutoRender: function() {
        var me = this;
        if (!me.rendered) {
            if (me.floating) { //浮动组件渲染到body元素
                me.render(document.body);
            } else { //如果配置了渲染目标元素,则渲染到目标;否则渲染到body
                me.render(Ext.isBoolean(me.autoRender) ? Ext.getBody() : me.autoRender);
            }
        }
    },
    
    doRenderFramingDockedItems: function (out, renderData, after) {
        //注意,该方法作为frameTpl的成员函数调用,this指向frameTpl
        var me = renderData.$comp;
        //大部分组件没有停靠子组件,所以检查其是否具有doRenderDockedItems()函数
        if (!me.rendered && me.doRenderDockedItems) {
            renderData.renderData.$skipDockedItems = true;
            //调用渲染停靠子组件的函数
            me.doRenderDockedItems.call(this, out, renderData, after);
        }
    },
    //完成该组件的渲染,所有子组件的onRender、afterRender会被调用,所有组件布局、容器布局的计算都会完成
    finishRender: function(containerIdx) {
        var me = this,
            tpl, data, contentEl, el, pre, hide;
        //如果尚未渲染,执行渲染(插入DOM)
        if (!me.el || me.$pid) {
            if (me.container) {
                el = me.container.getById(me.id, true);
            } else {
                el = Ext.getDom(me.id);
            }
            if (!me.el) {
                me.wrapPrimaryEl(el);
            } else {
                delete me.$pid;
                if (!me.el.dom) {
                    me.wrapPrimaryEl(me.el);
                }
                el.parentNode.insertBefore(me.el.dom, el);
                Ext.removeNode(el); //
            }
        } else if (!me.rendering) {
            tpl = me.initRenderTpl();
            if (tpl) {
                data = me.initRenderData();
                tpl.insertFirst(me.getTargetEl(), data);
            }
        }
        //否则,目前正在渲染
        if (!me.container) {
            me.container = Ext.get(me.el.dom.parentNode);
        }
        if (me.ctCls) {
            me.container.addCls(me.ctCls);
        }
        //调用onRender模板方法
        me.onRender(me.container, containerIdx);
        if (!me.overflowStyleSet) {
            me.getTargetEl().setStyle(me.getOverflowStyle());
        }
        me.el.setVisibilityMode(Ext.Element[me.hideMode.toUpperCase()]);
        if (me.overCls) {
            me.el.hover(me.addOverCls, me.removeOverCls, me);
        }
        //发布render事件
        if (me.hasListeners.render) {
            me.fireEvent('render', me);
        }
        if (me.contentEl) {
            pre = Ext.baseCSSPrefix;
            hide = pre + 'hide-';
            contentEl = Ext.get(me.contentEl);
            contentEl.removeCls([pre+'hidden', hide+'display', hide+'offsets', hide+'nosize']);
            me.getTargetEl().appendChild(contentEl.dom);
        }
        //调用afterRender模板方法,在这里会完成所有子组件的渲染
        me.afterRender();
        if (me.hasListeners.afterrender) {
            me.fireEvent('afterrender', me);
        }
        //初始化事件
        me.initEvents();
        //如果配置为隐藏,则隐藏组件
        if (me.hidden) {
            me.el.hide();
        }
    },
    //渲染子代,这里的默认实现是处理组件布局
    //对于容器类组件,还要处理容器布局
    finishRenderChildren: function () {
        var layout = this.getComponentLayout(); //得到组件布局
        layout.finishRender(); //由布局完成所有子代的渲染
    },
    //使用TABLE或者DIV模式获得外框的模板
    initFramingTpl: function(table) {
        var tpl = table ? this.getTpl('frameTableTpl') : this.getTpl('frameTpl');
        if (tpl && !tpl.applyRenderTpl) {
            this.setupFramingTpl(tpl);
        }
        return tpl;
    },
    //为外框模板添加一些成员函数
    setupFramingTpl: function(frameTpl) {
        frameTpl.applyRenderTpl = this.doApplyRenderTpl;
        frameTpl.renderDockedItems = this.doRenderFramingDockedItems;
    },
    //在配置的renderData基础上加上额外的属性
    initRenderData: function() {
        var me = this;
        return Ext.apply({
            $comp: me,
            id: me.id,
            ui: me.ui,
            uiCls: me.uiCls,
            baseCls: me.baseCls,
            componentCls: me.componentCls,
            frame: me.frame
        }, me.renderData);
    },
    //生命周期模板方法
    onRender: function(parentNode, containerIdx) {
        var me = this,
            x = me.x,
            y = me.y,
            lastBox, width, height,
            el = me.el,
            body = Ext.getBody().dom;
        if (Ext.scopeResetCSS && !me.ownerCt) {
            if (el.dom === body) {
                el.parent().addCls(Ext.resetCls);
            }
            else {
                // Floaters rendered into the body can all be bumped into the common reset element
                if (me.floating && me.el.dom.parentNode === body) {
                    Ext.resetElement.appendChild(me.el);
                }
                else {
                    me.resetEl = el.wrap(Ext.resetElementSpec, false, Ext.supports.CSS3LinearGradient ? undefined : '*');
                }
            }
        }
        me.applyRenderSelectors(); //处理子元素的引用,包括renderSelectors、childEls
        //设置组件状态为已渲染
        delete me.rendering;
        me.rendered = true;
        //记住最初的尺寸
        lastBox = null;
        if (x !== undefined) {
            lastBox = lastBox || {};
            lastBox.x = x;
        }
        if (y !== undefined) {
            lastBox = lastBox || {};
            lastBox.y = y;
        }
        if (!me.getFrameInfo() && Ext.isBorderBox) {
            width = me.width;
            height = me.height;
            if (typeof width == 'number') {
                lastBox = lastBox || {};
                lastBox.width = width;
            }
            if (typeof height == 'number') {
                lastBox = lastBox || {};
                lastBox.height = height;
            }
        }
        me.lastBox = me.el.lastBox = lastBox;
    },
    //确保组件被附加到document.body内部,而不是detachedBody
    ensureAttachedToBody: function (runLayout) {
        var comp = this,
            body;
        while (comp.ownerCt) {
            comp = comp.ownerCt;
        }
        if (comp.container.isDetachedBody) {
            comp.container = body = Ext.resetElement;
            body.appendChild(comp.el.dom);
            if (runLayout) {
                comp.updateLayout();
            }
            if (typeof comp.x == 'number' || typeof comp.y == 'number') {
                comp.setPosition(comp.x, comp.y);
            }
        }
    },
    wrapPrimaryEl: function (dom) {
        this.el = Ext.get(dom, true);
    },
    //初始化圆角外框
    initFrame : function() {
        if (Ext.supports.CSS3BorderRadius || !this.frame) {
            return;
        }
        var me = this,
            frameInfo = me.getFrameInfo(),
            frameWidth, frameTpl, frameGenId,
            i,
            frameElNames = me.frameElNames,
            len = frameElNames.length,
            suffix;
        if (frameInfo) {
            frameWidth = frameInfo.maxWidth;
            frameTpl = me.getFrameTpl(frameInfo.table);
            me.frameGenId = frameGenId = (me.frameGenId || 0) + 1;
            frameGenId = me.id + '-frame' + frameGenId;
            frameTpl.insertFirst(me.el, Ext.apply({
                $comp:      me,
                fgid:       frameGenId,
                ui:         me.ui,
                uiCls:      me.uiCls,
                frameCls:   me.frameCls,
                baseCls:    me.baseCls,
                frameWidth: frameWidth,
                top:        !!frameInfo.top,
                left:       !!frameInfo.left,
                right:      !!frameInfo.right,
                bottom:     !!frameInfo.bottom
            }, me.getFramePositions(frameInfo)));
            me.frameBody = me.el.down('.' + me.frameCls + '-mc');
            me.removeChildEls(function (c) {
                return c.id && me.frameIdRegex.test(c.id);
            });
            for (i = 0; i < len; i++) {
                suffix = frameElNames[i];
                me['frame' + suffix] = me.el.getById(frameGenId + suffix);
            }
        }
    },
    //更新圆角外框
    updateFrame: function() {
        if (Ext.supports.CSS3BorderRadius || !this.frame) {
            return;
        }
        var me = this,
            wasTable = this.frameSize && this.frameSize.table,
            oldFrameTL = this.frameTL,
            oldFrameBL = this.frameBL,
            oldFrameML = this.frameML,
            oldFrameMC = this.frameMC,
            newMCClassName;
        this.initFrame();
        if (oldFrameMC) {
            if (me.frame) {
                newMCClassName = this.frameMC.dom.className;
                oldFrameMC.insertAfter(this.frameMC);
                this.frameMC.remove();
                this.frameBody = this.frameMC = oldFrameMC;
                oldFrameMC.dom.className = newMCClassName;
                if (wasTable) {
                    me.el.query('> table')[1].remove();
                }
                else {
                    if (oldFrameTL) {
                        oldFrameTL.remove();
                    }
                    if (oldFrameBL) {
                        oldFrameBL.remove();
                    }
                    if (oldFrameML) {
                        oldFrameML.remove();
                    }
                }
            }
        }
        else if (me.frame) {
            this.applyRenderSelectors();
        }
    },
    //得到组件圆角外框的信息
    getFrameInfo: function() {
        //如果浏览器支持CSS3圆角边框,或者组件不使用外框,则返回,不去读取CSS样式
        if (Ext.supports.CSS3BorderRadius || !this.frame) {
            return false;
        }
        var me = this,
            frameInfoCache = me.frameInfoCache,
            el = me.el || me.protoEl,
            cls = el.dom ? el.dom.className : el.classList.join(' '),
            frameInfo = frameInfoCache[cls],//缓存的外框信息
            styleEl, left, top, info;
        //如果找不到缓存的外框信息        
        if (frameInfo == null) {
            //创建一个在屏幕可见范围外的承载样式的元素
            styleEl = Ext.fly(me.getStyleProxy(cls), 'frame-style-el');
            //设置背景图片偏移
            left = styleEl.getStyle('background-position-x');
            top = styleEl.getStyle('background-position-y');
            if (!left && !top) {
                info = styleEl.getStyle('background-position').split(' ');
                left = info[0];
                top = info[1];
            }
            //计算样式
            frameInfo = me.calculateFrame(left, top);
            if (frameInfo) {
                //确保不使用背景图片
                el.setStyle('background-image', 'none');
            }
            //加入缓存
            frameInfoCache[cls] = frameInfo;
        }
        me.frame = !!frameInfo;
        me.frameSize = frameInfo;
        return frameInfo;
    },
    //返回一个在屏幕可视范围外的基于cls样式的DIV
    getStyleProxy: function(cls) {
        var result = this.styleProxyEl || (Ext.AbstractComponent.prototype.styleProxyEl = Ext.resetElement.createChild({
                style: {
                    position: 'absolute',
                    top: '-10000px'
                }
            }, null, true));
        result.className = cls;
        return result;
    },
    //计算组件外框样式
    calculateFrame: function(left, top){
        if (!(parseInt(left, 10) >= 1000000 && parseInt(top, 10) >= 1000000)) {
            return false;
        }
        var max = Math.max,
            tl = parseInt(left.substr(3, 2), 10),
            tr = parseInt(left.substr(5, 2), 10),
            br = parseInt(top.substr(3, 2), 10),
            bl = parseInt(top.substr(5, 2), 10),
            frameInfo = {
                table: left.substr(0, 3) == '110',
                vertical: top.substr(0, 3) == '110',
                top:    max(tl, tr),
                right:  max(tr, br),
                bottom: max(bl, br),
                left:   max(tl, bl)
            };
        frameInfo.maxWidth = max(frameInfo.top, frameInfo.right, frameInfo.bottom, frameInfo.left);
        frameInfo.width = frameInfo.left + frameInfo.right;
        frameInfo.height = frameInfo.top + frameInfo.bottom;
        return frameInfo;
    },
    //获取圆角外框的位置信息
    getFramePositions: function(frameInfo) {
        var me = this,
            frameWidth = frameInfo.maxWidth,
            dock = me.dock,
            positions, tc, bc, ml, mr;
        if (frameInfo.vertical) {
            tc = '0 -' + (frameWidth * 0) + 'px';
            bc = '0 -' + (frameWidth * 1) + 'px';
            if (dock && dock == "right") {
                tc = 'right -' + (frameWidth * 0) + 'px';
                bc = 'right -' + (frameWidth * 1) + 'px';
            }
            positions = {
                tl: '0 -' + (frameWidth * 0) + 'px',
                tr: '0 -' + (frameWidth * 1) + 'px',
                bl: '0 -' + (frameWidth * 2) + 'px',
                br: '0 -' + (frameWidth * 3) + 'px',
                ml: '-' + (frameWidth * 1) + 'px 0',
                mr: 'right 0',
                tc: tc,
                bc: bc
            };
        } else {
            ml = '-' + (frameWidth * 0) + 'px 0';
            mr = 'right 0';
            if (dock && dock == "bottom") {
                ml = 'left bottom';
                mr = 'right bottom';
            }
            positions = {
                tl: '0 -' + (frameWidth * 2) + 'px',
                tr: 'right -' + (frameWidth * 3) + 'px',
                bl: '0 -' + (frameWidth * 4) + 'px',
                br: 'right -' + (frameWidth * 5) + 'px',
                ml: ml,
                mr: mr,
                tc: '0 -' + (frameWidth * 0) + 'px',
                bc: '0 -' + (frameWidth * 1) + 'px'
            };
        }
        return positions;
    },
    //获取圆角外框的XTemplate
    getFrameTpl : function(table) {
        return this.getTpl(table ? 'frameTableTpl' : 'frameTpl');
    },
    frameInfoCache: {}
});
Ext.AbstractComponent

私有类Ext.AbstractComponent为Sencha的RIA应用框架(例如Sencha Touch、ExtJS)提供了共享的功能,是ExtJS组件机制的基础。它提供了以下方面的功能:

  1. 规定了ExtJS组件的生命周期模型
  2. 提供了若干供扩展的模板方法,可以扩展ExtJS组件生命周期的各阶段,或者阻止默认生命周期行为的发生
  3. 规定了组件HTML内容的、基于模板的渲染机制
  4. 提供了各种设置组件UI样式的方法,包括CSS样式类、补白、边距、边框、可见性等
  5. 提供了组件布局机制
  6. 混入了:
    1. Ext.util.Renderable,保护组件渲染阶段的核心逻辑
    2. Ext.util.Observable,实现了观察者模式,是ExtJS事件机制的基础

AbstractComponent类包含以下成员:

配置项/属性/方法 说明
{autoEl}

String/Object,标签名或者DomHelper元素规格对象,用来创建表示该组件的封装元素(encapsulating element,即顶级元素)。该元素后续可以通过getEl()获得
对于Ext.Component、Ext.container.Container,默认值是div。复杂的组件通过指定renderTpls来生成元素

示例:

JavaScript
1
2
3
4
5
6
7
8
9
10
11
Ext.create('Ext.Component', {
    id: "widget",
    autoEl: {
        tag: 'h3',
        html: '<span>Content</span>'
    }
});
/* 输出内容 */
<h3 id="widget">
    <span>Content</span>
</h3>
{contentEl}

String,指定一个既有的HTML元素或者其id,作为当前组件的内容元素。用于把已有的DOM节点移动到组件的布局元素内部

可以用x-hidden、x-hide-display样式防止渲染前的闪烁

{html}

String/Object,HTML片段或者DomHelper元素规格对象,用作组件布局元素的内容。该HTML内容在组件被渲染后(render事件发布后)可用。在contentEl被插入前该HTML的内容被插入组件体

示例:

JavaScript
1
2
3
4
5
6
7
8
Ext.create('Ext.Component', {
    id: "widget",
    html: '<span>My Content</span>'
});
/* 输出内容 */
<div id="widget">
    <span>Content</span>
</div> 
{autoRender} Boolean/String/HTMLElement/Ext.Element,可以控制非浮动组件的自动渲染(在第一次show被调用时进行渲染),替代renderTo配置。对于浮动组件该配置无效,总是true
{renderTo} String/HTMLElement/Ext.Element,指定组件需要渲染到的HTML元素
{autoShow} Boolean,如果为真,组件创建后立即显示。仅用于使用autoRender的组件、浮动组件。对于浮动组件,如果该选项为false,即使作为某个容器的子组件,也不会自动渲染。
{baseCls} String,应用到组件封装元素的样式类,默认“x-component”
{cls} String,添加到组件封装元素的额外样式类
{componentCls} String,添加到组件根元素的样式类
{disabledCls} String,组件被禁用时的样式
{overCls} String,鼠标悬停组件时的额外样式类
{styleHtmlCls} String,styleHtmlContent配置为true时,用于指定内容目标(content target)的样式
{border} Number/String/Boolean,指定组件边框大小,支持类似10 5 3 10的CSS格式
{margin} Number/String/Boolean,指定组件外边距的大小,支持类似10 5 3 10的CSS格式
{padding} Number/String/Boolean,指定组件内补白的大小,支持类似10 5 3 10的CSS格式
{childEls} Object[],描述组件子元素(不是子组件)的数组,数组的每一个元素是包含3个属性的对象:
  1. name 子元素在组件中的属性名
  2. itemId 子元素的唯一标识,ExtJS自动将其与组件ID连接,因此不用担心唯一性
  3. id 子元素的ID

如果数组元素是字符串而不是对象,等价于:{ name: m, itemId: m }

举例:

JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Ext.create('Ext.Component', {
    renderTpl: [
        '<h1 id="{id}-title">{title}</h1>'
    ],
    renderData: {
        title: "Error"
    },
    childEls: ["title"],
    listeners: {
        afterrender: function(cmp){
            //在渲染完毕后,{id}-title元素自动作为当前组件的实例变量
            cmp.title.setStyle({color: "red"});
        }
    }
});
{componentLayout}

String/Object,组件布局。组件内部元素的大小、位置是由组件的布局管理器来负责的,当组件本身的大小发生改变时,组件布局管理器更新这些元素的大小和位置。

通常不需要初始化组件实例时指定该配置,具体组件类通常内置了组件布局方式

Ext.Component提供的默认组件布局,仅仅是将组件封装元素的宽高通过setSize()进行复制

{data}

Object,用于更新组件内容区域(content  area)的tpl模板的初始上下文。要使用创建组件时的配置对象作为上下文,可以:

JavaScript
1
2
3
4
5
constructor: function ( config ) {
    var me = this;
    me.data = config;
    me.callParent( config );
}
{tpl}

Ext.XTemplate/Ext.Template/String/String[],与data、tplWriteMode联用,更新组件的内容区域

如果不同时指定data,导致tpl中的内容不渲染,示例:

JavaScript
1
2
3
4
5
6
7
8
9
var widget = Ext.create('Ext.Component', {
    id: "widget",
    tpl: '<span>Hello {name}</span>',
    data: {name: 'Alex'}
});
/* 输出内容 */
<div id="widget">
    <span>Hello Alex</span>
</div>

使用tpl时,可利用的强大特性是模板中的占位符可以动态的被替换:

Shell
1
2
# 警告:update导致子元素被清空并重建,不是Angular那种风格
widget.update({name: 'Alex Wong'});

上面的语句导致HTML内容变为  <span>Hello Alex Wong</span> 

{tplWriteMode}

String,更新组件内容区域(content area)使用Ext.(X)Template的哪个函数?默认overwrite

{renderData}

Object,用于renderTpl模板的上下文,包含以下内置属性:id、ui、uiCls、baseCls、componentCls、frame

{renderTpl}

Ext.XTemplate/String/String[],一个XTemplate,用于创建组件封装元素的内部结构。

对于Ext.Component、Ext.container.Container,通常不需要指定该配置,如果该配置为null表示不渲染内部结构(getEl的得到的是空的没有子元素的元素)

{renderSelectors}

Object,包含DomQuery选择器的、用于识别组件子元素的对象。当组件内部结构通过renderTpl渲染后,该对象被遍历,找到的元素被作为当前组件的属性,该配置的功能与childEls类似,但是更加灵活,举例:

JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Ext.create( 'Ext.Component', {
    renderTpl : [
        '<h1 class="title">{title}</h1>', '<p>{desc}</p>'
    ],
    renderData : {
        title : "Error",
        desc : "Something went wrong"
    },
    renderSelectors : {
        titleEl : 'h1.title', //CSS选择器
        descEl : 'p'
    },
    listeners : {
        afterrender : function( cmp )
        {
            //在渲染完毕后,h1.title元素自动作为当前组件的实例变量
            cmp.titleEl.setStyle( {
                color : "red"
            } );
        }
    }
} );
{disabled}

Boolean,用于禁用组件

{draggable}

Boolean,用于支持组件的拖拽

{floating}

Boolean,用于指示组件为浮动,即独立出文档布局并使用绝对定位。该属性为true的组件称为浮动组件。

浮动组件的z-index由某个ZIndexManager的实例管理:

  1. 如果仅仅将组件渲染到DOM(而不是容器内),则 WindowManager管理其z-index
  2. 如果将组件渲染到容器内,其上溯到的第一个floating祖先容器的ZIndexManager负责管理组件的z-index
{frame}

Boolean,如果指定为true,在渲染阶段自动诸如一个外框元素到组件外围,提供一个圆角矩形框的视觉效果。仅仅用于诸如IE9-的遗留浏览器

{hidden} Boolean,组件是否是隐藏的
{height} Number,组件的高度,像素
{maxHeight} Number,组件允许的最大高度
{minHeight} Number,组件允许的最小高度
{width} Number,组件的宽度,像素
{maxWidth} Number,组件允许的最大宽度
{minWidth} Number,组件允许的最小宽度
{hideMode} 

String,隐藏的方式:

  1. display,使用display: none进行隐藏。该隐藏方式导致组件的尺寸为0
  2. visibility,使用visibility: hidden方式隐藏
  3. offsets,通过绝对定位将其移出文档可视区域,该隐藏方式保持组件尺寸不变
{id}

String,组件的唯一标识

{itemId}

String,条目标识,可以在不使用id的情况下用于获取组件(通过Container.getComponent),该属性在组件兄弟组件之间保持唯一性

{loader}

Ext.ComponentLoader/Object,用于远程加载组件内容的加载器对象

{plugins}

Object/Object[],使用的组件插件,插件用于提供定制的功能。对于插件只有一个要求:存在一个init方法,接受当前组件作为其参数

{shrinkWrap}

Boolean/Number,各值的意义:

  1. 如果值为0,宽、高都不依赖于内容,等价于false
  2. 如果值为1,宽依赖于内容(shrinkwrap),高不
  3. 如果值为2,高依赖于内容(shrinkwrap),宽不。这是默认值
  4. 如果值为3,宽、高都依赖于内容,等价于true
{style} String/Object,设置组件元素的样式,参数必须满足Ext.Element.applyStyles()的参数要求
{styleHtmlContent} Boolean,如果设置为true,自动为组件内容目标(content target,对于panel来说是body)添加样式,
{ui} String,用于定制ExtJS组件的主题
{xtype} Sring,组件的别名,用于延迟创建
_isLayoutRoot protected Boolean,设置该值为true,可以导致isLayoutRoot()返回true
autoGenId private Boolean,指示组件标识是自动生成而不是手工指定的
componentLayoutCounter private Number,该组件的组件布局被调用的次数
draggable readonly Boolean,是否可拖拽
frameSize readonly Object,圆角外框的尺寸,参frame配置
isComponent Boolean,用于Duck类型识别,判断一个对象是否组件
maskOnDisable Boolean,当禁用时,是否显示一个半透明遮罩。FieldContainer, FieldSet, Field, Button, Tab等组件覆盖此标记,以提供定制的禁用逻辑
ownerCt readonly Ext.Container,组件所在容器
rendered  readonly Boolean,指示组件是否已经渲染
addCls() Ext.Component ( String/String[] cls ),添加样式到组件的顶级元素
removeCls Ext.Component ( String/String[] cls ) ,从顶级元素移除样式
addClsWithUI() void ( String/String[] classes, Object skip ),添加样式到uiCls数组,该方法会同时调用addUIClsToElement把样式应用到组件所有元素
removeClsWithUI void ( String/String[] cls )
addFocusListener() private,如果元素具有focusEl,则添加focus监听器
addPropertyToState() Boolean ( Object state, String propName, [String value] ),如果属性值与默认/配置值不一样,将其存放到状态对象state中
constructPlugins( )  private,返回完整创建的插件实例数组
convertPosition() privaet void( Object pos, Object withUnits ),把数字形式的位置添加px后缀
destroy() 销毁组件
disable() void ( [Boolean silent] ),禁用组件,参数用于避免disable事件的触发
doComponentLayout() Ext.container.Container(),执行组件布局,当组件内容改变,需要调整子元素大小位置时,应当调用该方法,返回this
enable() void ( [Boolean silent] ),启用组件,参数用于避免enable事件的触发
getBubbleTarget( ) Ext.container.Container(),返回事件冒泡的目标 
getEl() Ext.dom.Element(),返回组件的顶层元素
getFocusEl() private,获取该组件用于持有焦点的元素,默认返回null
getHeight( ) Number(),获取元素的高度,类似的方法包括getWidth()、getSize( ) 
getId( )   String(),获取组件ID
getItemId( ) String(),获取分配给组件的itemId, 如果没有返回id
getPlugin() Ext.AbstractPlugin (String pluginId),根据id获取插件对象
getSizeModel() Object ( Object ownerCtSizeModel ) 返回一个用于描述组件宽高如何被管理的SizeModel对象
getState( ) Object(),获取组件状态对象,默认实现返回flex, anchor, width, height, collapsed等尺寸相关的属性
getTargetEl() private,用于确定在何处插入html、contentEl、items
hasCls() Boolean (String cls),判断指定的CSS样式类是否存在于组件的元素
hasUICls() Boolean (String cls),判断当前是否应用了指定的uiCls
isDisabled( ) 组件是否被禁用
isDraggable( ) 组件是否可以作为拖拽的源
isDroppable( )  组件是否可以作为拖拽的目标
isFloating( ) 是否浮动组件
isHidden( ) 组件是否被隐藏
isLayoutRoot( ) protected Boolean(),组件是否是布局的根。如果组件可以在没有父容器的配合/不对父容器产生影响的情况下执行布局,则可以返回true
isLayoutSuspended( ) 当前组件的布局是否被暂停
isVisible() 组件是否可见
isXType Boolean ( String xtype, [Boolean shallow] ),判断当前组件是否是指定xtype的实例,如果shallow=true,则必须精确匹配,否则当前组件可以是xtype的子类型
on() addListener()的别名
registerFloatingItem void ( Object cmp ),由Component.doAutoRender方法,使用当前组件的ZIndexManager管理cmp
setBorder() void ( String/Number border ),设置边框
setDisabled() void ( Boolean disabled ),设置禁用状态
setDocked() Ext.Component( Object dock, [Boolean layoutParent] ) ,设置当前组件在其父容器的停靠位置,仅当该组件是父容器dockedItems的一部分时有效
setHeight() Ext.Component( Number height ),设置高度,类似还有setWidth()、setSize()
setPosition Ext.Component( Number left, Number top, [Boolean/Object animate] ),设置组件的位置
setUI() 设置使用的主题样式
setVisible() 设置组件可见性
update()

void ( String/Object htmlOrData, [Boolean loadScripts], [Function callback] ),更新组件的内容区域

可以导致组件内容模板中的占位符自动更新

updateLayout() void ( Object options ),更新组件的布局,如果该操作影响ownerCt,则转调ownerCt的updateLayout(),否则仅仅调用该组件的updateLayout()本身
静态方法
flushLayouts( ) 执行所有因为 suspendLayouts而暂停的布局
resumeLayouts() void ( [Boolean flush] ),恢复整个框架的布局活动
suspendLayouts( ) 暂停整个框架的布局活动
updateLayout() void ( Ext.Component comp, [Boolean defer] ),更新某个组件的布局
组件查询方法  
is() Boolean ( String selector ),检测当前组件是否与选择器匹配
isDescendantOf() Boolean ( Ext.Container container ) ,检测当前组件是否为指定容器的后代
nextNode() Ext.Component ( [String selector] ) ,获取组件树结构中的下一个(匹配选择器的)组件(按树遍历顺序)
nextSibling() Ext.Component ( [String selector] ),获取组件的下一个(匹配选择器的)兄弟组件。别名:next
previousNode() Ext.Component ( [String selector] ),获取组件树结构中的上一个(匹配选择器的)组件(按树遍历顺序)
previousSibling() Ext.Component ( [String selector] ),获取组件的上一个(匹配选择器的)兄弟组件。别名:prev
up() Ext.container.Container( [String selector] ),根据选择器向上查询容器
模板方法  
afterComponentLayout() void ( Number width, Number height, Number oldWidth, Number oldHeight ),当组件布局被执行后,由布局系统调用
afterSetPosition()  void ( Number x, Number y ),当组件的位置被设置了,该方法被调用
beforeComponentLayout() Boolean ( Number adjWidth, Number adjHeight ),当组件布局被执行前调用,返回false导致不进行组件布局
beforeDestroy() 在组件被销毁前调用
beforeSetPosition() private void ( Object x, Object y, Object animate ),在设置组件位置前调用
onAdded() void ( Ext.container.Container container, Number pos ),该方法用于感知组件被加入的容器,会触发一个add事件。对父容器的引用建立在添加阶段,而不是渲染阶段。在此方法被调用时,组件被置于父容器的items中
onDisable( ) 添加额外的禁用逻辑,在调用被覆盖版本后,组件禁用
onEnable( ) 添加额外的启用逻辑,在调用被覆盖版本后,组件启用
onPosition() void ( Number x, Number y ),组件被移动后调用,默认实现时空的
onRemoved() void ( Boolean destroying ),用于感知组件被从父容器中移除,会发布一个removed事件。在此方法被调用时,组件已经移出父容器的items,但是尚未destroy,调用覆盖版本后,ownerCt、refOwner 置空
onResize( ) 添加额外的改变大小逻辑
⚡activate void ( Ext.Component this, Object eOpts ),组件被视觉上激活后触发
⚡added void ( Ext.Component this, Ext.container.Container container, Number pos, Object eOpts ),组件被添加到容器后触发 
⚡afterrender  void ( Ext.Component this, Object eOpts ),在组件被渲染之后触发,此时afterRender()已经调用
⚡beforeactivate Boolean ( Ext.Component this, Object eOpts ),组件激活前触发,返回false阻止激活 
⚡beforedeactivate Boolean ( Ext.Component this, Object eOpts ),组件去激活前触发,返回false阻止去激活
⚡beforedestroy Boolean ( Ext.Component this, Object eOpts ),组件销毁前触发,返回false阻止销毁
⚡beforehide Boolean ( Ext.Component this, Object eOpts ),组件渲染前触发,返回false阻止渲染
⚡beforeshow Boolean ( Ext.Component this, Object eOpts ),组件显示前触发,返回false阻止显示 
⚡blur void( Ext.Component this, Ext.EventObject The, Object eOpts ),组件失去焦点时触发 
⚡boxready  void( Ext.Component this, Number width, Number height, Object eOpts ),组件在第一次以初始尺寸布局后发布一次 
⚡deactivate void ( Ext.Component this, Object eOpts ),组件在视觉上去激活时触发 
⚡destroy void ( Ext.Component this, Object eOpts ),组件被销毁后触发 
⚡disable void ( Ext.Component this, Object eOpts ),组件禁用后触发 
⚡enable void ( Ext.Component this, Object eOpts ),组件启用后触发 
⚡focus  void ( Ext.Component this, Ext.EventObject The, Object eOpts ) 组件接收到焦点后触发 
⚡hide void ( Ext.Component this, Object eOpts ),组件被隐藏后触发 
⚡move void ( Ext.Component this, Number x, Number y, Object eOpts ),组件的位置改变后触发 
⚡removed  void ( Ext.Component this, Ext.container.Container ownerCt, Object eOpts ),组件从容器中移除后触发
⚡render  void ( Ext.Component this, Object eOpts ),当组件的HTML标记被渲染到DOM后触发,注意组件不一定展示(show) 
⚡resize void ( Ext.Component this, Number width, Number height, Number oldWidth, Number oldHeight, Object eOpts ),当组件的大小被改变时触发。注意:组件第一次使用其初始大小布局后,不会触发该事件,此情况可以使用boxready代替该事件 
⚡show  void ( Ext.Component this, Object eOpts ),当调用show()方法后,当前组件展示,该事件被触发

Ext.AbstractComponent的源代码分析如下:

Ext.AbstractComponent
JavaScript
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
Ext.define('Ext.AbstractComponent', {
    statics: {
        AUTO_ID: 1000,
        pendingLayouts: null,
        layoutSuspendCount: 0,
        cancelLayout: function(comp, isDestroying) {
            //context为布局上下文,Ext.layout.Context的实例
            var context = this.runningLayoutContext || this.pendingLayouts;
            if (context) {
                context.cancelComponent(comp, false, isDestroying);
            }
        },
        flushLayouts: function () {
            var me = this,
                context = me.pendingLayouts;
            //invalidQueue是布局失效的顶层布局组件的队列,队列里的组件不会存在具有父子/前后代关系的组件
            if (context && context.invalidQueue.length) {
                me.pendingLayouts = null;
                me.runningLayoutContext = context; //正在执行的布局上下文
                //覆盖上下文的刷新布局方法
                Ext.override(context, {
                    runComplete: function () {
                        //在通过runComplete调用finishedLayout()前必须清空布局队列
                        //因为finishedLayout()会调用afterComponentLayout,而可能导致重新进入doLayout或doComponentLayout
                        me.runningLayoutContext = null;
                        return this.callParent();
                    }
                });
                //执行布局计算,此方法只能在布局上下文上调用一次
                context.run();
            }
        },
        //递减暂停布局计数器,如果为0,则刷新布局
        resumeLayouts: function (flush) {
            if (this.layoutSuspendCount && ! --this.layoutSuspendCount) {
                if (flush) {
                    this.flushLayouts();
                }
            }
        },
        //递增暂停布局计数器
        suspendLayouts: function () {
            ++this.layoutSuspendCount;
        },
        //更新组件的布局
        updateLayout: function (comp, defer) {
            var me = this,
                running = me.runningLayoutContext,
                pending;
            //在running或者pending布局的下一次周期中排队
            if (running) {
                running.queueInvalidate(comp);
            } else {
                pending = me.pendingLayouts || (me.pendingLayouts = new Ext.layout.Context());
                pending.queueInvalidate(comp);
                if (!defer && !me.layoutSuspendCount && !comp.isLayoutSuspended()) {
                    me.flushLayouts();
                }
            }
        }
    },
    isComponent: true,
    getAutoId: function() {
        this.autoGenId = true;
        return ++Ext.AbstractComponent.AUTO_ID;
    },
    deferLayouts: false,
    autoGenId: false,
    //默认的渲染模板
    renderTpl: '{%this.renderContent(out,values)%}',
    frameSize: { left: 0, top: 0, right: 0, bottom: 0, width: 0, height: 0 },
    tplWriteMode: 'overwrite',
    baseCls: Ext.baseCSSPrefix + 'component',
    disabledCls: Ext.baseCSSPrefix + 'item-disabled',
    ui: 'default',
    uiCls: [],
    hidden: false,
    disabled: false,
    draggable: false,
    floating: false,
    hideMode: 'display',
    styleHtmlContent: false,
    styleHtmlCls: Ext.baseCSSPrefix + 'html',
    autoShow: false,
    autoRender: false,
    // @private
    allowDomMove: true,
    rendered: false,
    componentLayoutCounter: 0,
    shrinkWrap: 2,
    weight: 0,
    maskOnDisable: true,
    _isLayoutRoot: false,
    //组件构造器,规定了组件的生命周期
    constructor : function(config) {
        var me = this,
            i, len, xhooks;
        //配置项作为属性,应用到当前对象
        if (config) {
            Ext.apply(me, config);
            //xhooks用于动态的覆盖一个实例的成员,而不是类的
            xhooks = me.xhooks;
            if (xhooks) {
                delete me.xhooks;
                Ext.override(me, xhooks);
            }
        } else {
            config = {};
        }
        //初始配置
        me.initialConfig = config;
        //构造混入
        me.mixins.elementCt.constructor.call(me);
        me.addEvents(
            'beforeactivate',
            'activate',
            'beforedeactivate',
            'deactivate',
            'added',
            'disable',
            'enable',
            'beforeshow',
            'show',
            'beforehide',
            'hide',
            'removed',
            'beforerender',
            'render',
            'afterrender',
            'boxready',
            'beforedestroy',
            'destroy',
            'resize',
            'move',
            'focus',
            'blur'
        );
        //触发以生成ID
        me.getId();
        //生成protoEl属性,Ext.util.ProtoElement用于在渲染前管理Element的属性,例如class和style
        me.setupProtoEl();
        if (me.cls) {
            me.initialCls = me.cls;
            me.protoEl.addCls(me.cls);
        }
        if (me.style) {
            me.initialStyle = me.style;
            me.protoEl.setStyle(me.style);
        }
        //受管的监听器列表
        me.mons = [];
        me.renderData = me.renderData || {};
        me.renderSelectors = me.renderSelectors || {};
        //如果配置了插件,则在此执行所有插件的构造
        if (me.plugins) {
            me.plugins = me.constructPlugins();
        }
        if (!me.hasListeners) {
            me.hasListeners = new me.HasListeners();
        }
        //生命周期模板方法:初始化组件
        me.initComponent();
        //在组件管理器中注册自己
        Ext.ComponentManager.register(me);
        //构造混入类
        me.mixins.observable.constructor.call(me);
        me.mixins.state.constructor.call(me, config);
        //resize作为状态事件,resize将导致组件状态被保存
        this.addStateEvents('resize');
        //如果配置了插件,则在此依次初始化插件
        if (me.plugins) {
            for (i = 0, len = me.plugins.length; i < len; i++) {
                me.plugins[i] = me.initPlugin(me.plugins[i]);
            }
        }
        //初始化用于远程加载组件内容的加载器
        me.loader = me.getLoader();
 
        //生命周期的第二阶段:渲染
        //容器中的子组件,其渲染阶段被父容器控制
        //如果配置了renderTo,那么立即执行渲染
        if (me.renderTo) {
            me.render(me.renderTo);
        }
        //如果配置了autoShow,并且不处于容器内,那么立即执行展示
        if (me.autoShow && !me.isContained) {
            me.show();
        }
        if (Ext.isDefined(me.disabledClass)) {
            if (Ext.isDefined(Ext.global.console)) {
                Ext.global.console.warn('Ext.Component: disabledClass has been deprecated. Please use disabledCls.');
            }
            me.disabledCls = me.disabledClass;
            delete me.disabledClass;
        }
    },
 
    initComponent: function () {
        //再一次调用了constructPlugins(),可以让子类在自己的initComponent()里面添加新的插件配置
        this.plugins = this.constructPlugins();
        //设置组件的初始大小
        this.setSize(this.width, this.height);
    },
    //获取组件的状态,例如大小、位置等信息,子类可以扩展以添加任何定制的、需要持久化的信息
    getState: function() {
        var me = this,
            state = null,
            sizeModel = me.getSizeModel();
 
        if (sizeModel.width.configured) {
            state = me.addPropertyToState(state, 'width');
        }
        if (sizeModel.height.configured) {
            state = me.addPropertyToState(state, 'height');
        }
        return state;
    },
    //把某个属性添加到状态中
    addPropertyToState: function (state, propName, value) {
        var me = this,
            len = arguments.length;
        if (len == 3 || me.hasOwnProperty(propName)) {
            if (len < 3) {
                value = me[propName];
            }
            if (value !== me.initialConfig[propName]) {
                (state || (state = {}))[propName] = value;
            }
        }
        return state;
    },
    //如何展示组件,缺省适配
    show: Ext.emptyFn,
    //生命周期模板方法:隐藏组件时
    //缺省实现:更新布局
    onHide: function() {
        this.updateLayout({ isRoot: false });
    },
    //生命周期模板方法:显示组件时
    //缺省实现:更新布局
    onShow : function() {
        this.updateLayout({ isRoot: false });
    },
    //构造一个插件,如果已经构造,简单的返回查询对象
    constructPlugin: function(plugin) {
        
        if (plugin.ptype && typeof plugin.init != 'function') {
            plugin.cmp = this;
            plugin = Ext.PluginManager.create(plugin);
        }
        else if (typeof plugin == 'string') {
            plugin = Ext.PluginManager.create({
                ptype: plugin,
                cmp: this
            });
        }
        return plugin;
    },
    //构造所有配置的插件
    constructPlugins: function() {
        var me = this,
            plugins,
            result = [],
            i, len;
 
        if (me.plugins) {
            plugins = Ext.isArray(me.plugins) ? me.plugins : [ me.plugins ];
            for (i = 0, len = plugins.length; i < len; i++) {
                result[i] = me.constructPlugin(plugins[i]);
            }
            return result;
        }
    },
    // @private
    //初始化插件,就是调用插件的init方法并入参当前组件
    initPlugin : function(plugin) {
        plugin.init(this);
 
        return plugin;
    },
    updateAria: Ext.emptyFn,
    //使用当前组件作为参数组件的浮动管理器
    registerFloatingItem: function(cmp) {
        var me = this;
        if (!me.floatingDescendants) {
            me.floatingDescendants = new Ext.ZIndexManager(me);
        }
        me.floatingDescendants.register(cmp);
    },
    unregisterFloatingItem: function(cmp) {
        var me = this;
        if (me.floatingDescendants) {
            me.floatingDescendants.unregister(cmp);
        }
    },
    //布局暂停计数器
    layoutSuspendCount: 0,
    //暂停当前组件的布局
    suspendLayouts: function () {
        var me = this;
        if (!me.rendered) { //如果当前组件尚未渲染,不做任何处理
            return;
        }
        if (++me.layoutSuspendCount == 1) {
            me.suspendLayout = true;
        }
    },
    //恢复当前组件的布局
    resumeLayouts: function (flushOptions) {
        var me = this;
        if (!me.rendered) { //如果当前组件尚未渲染,不做任何处理
            return;
        }
        if (! --me.layoutSuspendCount) {
            //如果计数器为0,那么需要更新当前组件的布局
            me.suspendLayout = false;
            if (flushOptions && !me.isLayoutSuspended()) {
                me.updateLayout(flushOptions);
            }
        }
    },
    //初始化事件的私有方法
    initEvents : function() {
        var me = this,
            afterRenderEvents = me.afterRenderEvents,
            el, //注意这个闭包变量 ➀
            property,
            //为元素添加受管的监听器
            fn = function(listeners){
                //回调函数立即使用闭包变量的实时值 ➃
                me.mon(el, listeners);
            };
        //渲染后事件映射:{元素属性:[监听器1,监听器2]}
        if (afterRenderEvents) {
            for (property in afterRenderEvents) {
                if (afterRenderEvents.hasOwnProperty(property)) {
                    el = me[property]; //闭包变量的值改变 ➁
                    if (el && el.on) {
                        //执行回调函数 ➂
                        Ext.each(afterRenderEvents[property], fn);
                    }
                }
            }
        }
        me.addFocusListener();
    },
    //添加焦点监听器
    addFocusListener: function() {
        var me = this,
            focusEl = me.getFocusEl(),//得到当前组件的焦点元素
            needsTabIndex;
        //不仅仅是表单,所有容器均可能是支持获取焦点,例如面板、工具栏、窗口
        //通常,作为焦点元素的DIV元素无法接收焦点,但如果调用了FocusManager,则其非缺省的导航处理器(navigation handlers )
        //将明确的使面板、容器或者FieldSet获取焦点
        
        //如果当前组件具有焦点元素,可能需要为其添加focus、blur事件:
        if (focusEl) {
            // getFocusEl()方法可能返回一个组件,如果作为容器的this希望把焦点管理代理给它的子代组件
            // Window组件可以通过配置defaultFocus为它的一个按钮来实现这种代理
            if (focusEl.isComponent) {
                return focusEl.addFocusListener(); //代理
            }
            //如果焦点元素本身就是可聚焦的(focusable),那么总是需要添加focus监听器处理组件的聚焦
            //如果焦点元素是不可聚集的,除非启用了FocusManager,否则不需要添加focus监听器
            needsTabIndex = focusEl.needsTabIndex();
            if (!me.focusListenerAdded && (!needsTabIndex || Ext.FocusManager.enabled)) {
                if (needsTabIndex) {
                    focusEl.dom.tabIndex = -1;
                }
                focusEl.on({
                    focus: me.onFocus,
                    blur: me.onBlur,
                    scope: me
                });
                me.focusListenerAdded = true;
            }
        }
    },
    //获取当前组件关联的焦点元素,缺省适配
    getFocusEl: Ext.emptyFn,
    //是否可聚焦
    isFocusable: function(c) {
        var me = this,
            focusEl;
        if ((me.focusable !== false) && (focusEl = me.getFocusEl()) && me.rendered && !me.destroying && !me.isDestroyed && !me.disabled && me.isVisible(true)) {
            if (focusEl.isComponent) {
                return focusEl.isFocusable();
            }
 
            return focusEl && focusEl.dom && focusEl.isVisible();
        }
    },
    //聚焦前模板方法
    preFocus: Ext.emptyFn,
    //聚焦时模板方法
    onFocus: function(e) {
        var me = this,
            focusCls = me.focusCls,
            focusEl = me.getFocusEl();
        if (!me.disabled) {
            me.preFocus(e);
            if (focusCls && focusEl) {
                focusEl.addCls(me.addClsWithUI(focusCls, true));//添加聚焦CSS样式
            }
            if (!me.hasFocus) {
                me.hasFocus = true;
                me.fireEvent('focus', me, e); //发布聚焦事件
            }
        }
    },
    //失焦前模板方法
    beforeBlur : Ext.emptyFn,
    //失焦时模板方法
    onBlur : function(e) {
        var me = this,
            focusCls = me.focusCls,
            focusEl = me.getFocusEl();
 
        if (me.destroying) {
            return;
        }
 
        me.beforeBlur(e);
        if (focusCls && focusEl) {
            focusEl.removeCls(me.removeClsWithUI(focusCls, true));//移除聚焦CSS样式
        }
        if (me.validateOnBlur) {
            me.validate();    //执行验证
        }
        me.hasFocus = false;
        me.fireEvent('blur', me, e); //发布失焦事件
        me.postBlur(e);
    },
    //失焦后模板方法
    postBlur : Ext.emptyFn,
    getId : function() {
        return this.id || (this.id = 'ext-comp-' + (this.getAutoId()));
    },
    //得到当前组件的itemId,如果没有,则返回id
    getItemId : function() {
        return this.itemId || this.id;
    },
    //得到当前组件的封装(顶级)元素
    getEl : function() {
        return this.el;
    },
    //用于确定html、contentEl、items等HTML插入到哪边
    getTargetEl: function() {
        return this.frameBody || this.el;//如果具有frameBody,则frameBody作为targetEl,否则使用顶级元素
    },
 
    /**
     * 更新当前组件的内容区域(即targetEl)
     * @param {String/Object} htmlOrData 如果组件配置了tpl模板,则使用该参数作为上下文填充模板,否则作为HTML更新内如区域
     * @param {Boolean} [loadScripts=false] 仅当使用html配置时合法
     * @param {Function} [callback] 仅当使用html配置时合法,加载脚本完毕后的回调
     */
    update : function(htmlOrData, loadScripts, cb) {
        var me = this;
 
        if (me.tpl && !Ext.isString(htmlOrData)) {
            me.data = htmlOrData;
            if (me.rendered) {
                //填充模板,并覆盖targetEl的innerHTML
                me.tpl[me.tplWriteMode](me.getTargetEl(), htmlOrData || {});
            }
        } else {
            me.html = Ext.isObject(htmlOrData) ? Ext.DomHelper.markup(htmlOrData) : htmlOrData;
            if (me.rendered) {
                //更新元素的innerHTML
                me.getTargetEl().update(me.html, loadScripts, cb);
            }
        }
        if (me.rendered) {
            //更新组件的布局
            me.updateLayout();
        }
    },
    //模板方法:第一次使用初始大小渲染后
    onBoxReady: function(){
        var me = this;
        if (me.disableOnBoxReady) {
            me.onDisable();
        } else if (me.enableOnBoxReady) {
            me.onEnable();
        }
        //初始化:可改变大小
        if (me.resizable) {
            me.initResizable(me.resizable);
        }
        //初始化:可拖拽。必须在可改变大小之后
        if (me.draggable) {
            me.initDraggable();
        }
    },
    //启用组件
    enable: function(silent) {
        var me = this;
        delete me.disableOnBoxReady;
        me.removeCls(me.disabledCls);
        if (me.rendered) {
            me.onEnable();
        } else {
            me.enableOnBoxReady = true;
        }
        me.disabled = false;
        delete me.resetDisable;
        //silent用于阻止事件触发
        if (silent !== true) {
            me.fireEvent('enable', me);
        }
        return me;
    },
    //禁用组件
    disable: function(silent) {
        var me = this;
        delete me.enableOnBoxReady;
        me.addCls(me.disabledCls);
        if (me.rendered) {
            me.onDisable();
        } else {
            me.disableOnBoxReady = true;
        }
        me.disabled = true;
        //silent用于阻止事件触发
        if (silent !== true) {
            delete me.resetDisable;
            me.fireEvent('disable', me);
        }
 
        return me;
    },
    //模板方法:在启用后执行
    //缺省实现:去除遮罩
    onEnable: function() {
        if (this.maskOnDisable) {
            this.el.dom.disabled = false;
            this.unmask();
        }
    },
    //模板方法:在禁用后执行
    onDisable : function() {
        var me = this,
            focusCls = me.focusCls,
            focusEl = me.getFocusEl();
        //清除聚焦
        if (focusCls && focusEl) {
            focusEl.removeCls(me.removeClsWithUI(focusCls, true));
        }
        //添加遮罩
        if (me.maskOnDisable) {
            me.el.dom.disabled = true;
            me.mask();
        }
    },
    //添加遮罩
    mask: function() {
        var box = this.lastBox,
            target = this.getMaskTarget(),
            args = [];
        if (box) {
            args[2] = box.height;
        }
        target.mask.apply(target, args);
    },
    //清除遮罩
    unmask: function() {
        this.getMaskTarget().unmask();
    },
    //遮罩的目标元素
    getMaskTarget: function(){
        return this.el;
    },
    //添加监听器
    addListener : function(element, listeners, scope, options) {
        var me = this,
            fn,
            option;
        if (Ext.isString(element) && (Ext.isObject(listeners) || options && options.element)) {
            if (options.element) {
                fn = listeners;
 
                listeners = {};
                listeners[element] = fn;
                element = options.element;
                if (scope) {
                    listeners.scope = scope;
                }
                for (option in options) {
                    if (options.hasOwnProperty(option)) {
                        if (me.eventOptionsRe.test(option)) {
                            listeners[option] = options[option];
                        }
                    }
                }
            }
            if (me[element] && me[element].on) {
                me.mon(me[element], listeners);
            } else {
                me.afterRenderEvents = me.afterRenderEvents || {};
                if (!me.afterRenderEvents[element]) {
                    me.afterRenderEvents[element] = [];
                }
                me.afterRenderEvents[element].push(listeners);
            }
        }
        return me.mixins.observable.addListener.apply(me, arguments);
    },
    //移除受管的事件监听器
    removeManagedListenerItem: function(isClear, managedListener, item, ename, fn, scope){
        var me = this,
            element = managedListener.options ? managedListener.options.element : null;
 
        if (element) {
            element = me[element];
            if (element && element.un) {
                if (isClear || (managedListener.item === item && managedListener.ename === ename && (!fn || managedListener.fn === fn) && (!scope || managedListener.scope === scope))) {
                    element.un(managedListener.ename, managedListener.fn, managedListener.scope);
                    if (!isClear) {
                        Ext.Array.remove(me.managedListeners, managedListener);
                    }
                }
            }
        } else {
            return me.mixins.observable.removeManagedListenerItem.apply(me, arguments);
        }
    },
    //模板方法:被添加到容器后
    //缺省实现:发布added事件
    onAdded : function(container, pos) {
        var me = this;
        me.ownerCt = container;
        if (me.hasListeners.added) {
            me.fireEvent('added', me, container, pos);
        }
    },
    //模板方法:被从容器中移除后
    //缺省实现:发布removed事件
    onRemoved : function(destroying) {
        var me = this;
        if (me.hasListeners.removed) {
            me.fireEvent('removed', me, me.ownerCt);
        }
        delete me.ownerCt;     //删除父容器属性
        delete me.ownerLayout; //删除父布局属性
    },
    //生命周期模板方法:在销毁前的动作
    //返回false可以取消销毁
    beforeDestroy : Ext.emptyFn,
    //模板方法,当修改了组件大小后
    onResize : Ext.emptyFn,
    //设置组件大小
    setSize : function(width, height) {
        var me = this;
        if (width && typeof width == 'object') {
            height = width.height;
            width  = width.width;
        }
        if (typeof width == 'number') {
            me.width = Ext.Number.constrain(width, me.minWidth, me.maxWidth);
        } else if (width === null) {
            delete me.width;
        }
        if (typeof height == 'number') {
            me.height = Ext.Number.constrain(height, me.minHeight, me.maxHeight);
        } else if (height === null) {
            delete me.height;
        }
        //如果尚未渲染,仅仅设置属性即可,否则:
        if (me.rendered && me.isVisible()) {
            //如果改变了当前组件的大小,那么就不能作为布局的根,因为对父容器会产生影响
            //更新布局
            me.updateLayout({
                isRoot: false
            });
        }
        return me;
    },
    //判断当前组件是否布局的根    
    isLayoutRoot: function() {
        var me = this,
            ownerLayout = me.ownerLayout;
        //对于浮动组件,或者明确设置了_isLayoutRoot的组件,返回true
        if (!ownerLayout || me._isLayoutRoot || me.floating) {
            return true;
        }
        //让父布局判断当前组件是否布局根
        return ownerLayout.isItemLayoutRoot(me);
    },
    //判断布局是否被暂停
    //只要当前组件或者其任意布局祖代的布局被暂停,则返回true
    isLayoutSuspended: function () {
        var comp = this,
            ownerLayout;
        while (comp) {
            if (comp.layoutSuspendCount || comp.suspendLayout) {
                return true;
            }
 
            ownerLayout = comp.ownerLayout;
            if (!ownerLayout) {
                break;
            }
            comp = ownerLayout.owner;
        }
 
        return false;
    },
    //更新当前组件的布局
    updateLayout: function (options) {
        var me = this,
            defer,
            isRoot = options && options.isRoot;
 
        if (!me.rendered || me.layoutSuspendCount || me.suspendLayout) {
            return;
        }
        //对于隐藏组件,取消布局
        if (me.hidden) {
            Ext.AbstractComponent.cancelLayout(me);
        } else if (typeof isRoot != 'boolean') {
            isRoot = me.isLayoutRoot();
        }
        // 如果当前组件不是布局根,且父布局关心当前组件的尺寸变化,那么父布局会统一处理
        if (isRoot || !me.ownerLayout || !me.ownerLayout.onContentChange(me)) {
            //否则,自行处理
            if (!me.isLayoutSuspended()) {
                defer = (options && options.hasOwnProperty('defer')) ? options.defer : me.deferLayouts;
                Ext.AbstractComponent.updateLayout(me, defer); //调用静态函数执行布局
            }
        }
    },
    //执行组件布局,该方法开放调用,任何时候,改变组件内容均需要考虑调用
    doComponentLayout : function() {
        this.updateLayout();
        return this;
    },
    //重设布局对象
    setComponentLayout : function(layout) {
        var currentLayout = this.componentLayout;//当前布局是componentLayout属性
        if (currentLayout && currentLayout.isLayout && currentLayout != layout) {
            currentLayout.setOwner(null); //取消当前布局对象的关联
        }
        this.componentLayout = layout;
        layout.setOwner(this); //注意布局对象是被组件独占的
    },
    //获取当前的组件布局,如果不存在,则创建之
    getComponentLayout : function() {
        var me = this;
        if (!me.componentLayout || !me.componentLayout.isLayout) {
            me.setComponentLayout(Ext.layout.Layout.create(me.componentLayout, 'autocomponent'));
        }
        return me.componentLayout;
    },
    //组件布局后调用的模板方法
    //缺省实现
    afterComponentLayout: function(width, height, oldWidth, oldHeight) {
        var me = this,
            floaters, len, i, floater;
        if (++me.componentLayoutCounter === 1) {
            me.afterFirstLayout(width, height);//调用第一次组件布局的模板方法
        }
        //如果浮动子组件没有显示,立即显示
        if (me.floatingItems) {
            floaters = me.floatingItems.items;
            len = floaters.length;
            for (i = 0; i < len; i++) {
                floater = floaters[i];
                if (!floater.rendered && floater.autoShow) {
                    floater.show();
                }
            }
        }
        //如果大小发生改变,发布resize事件
        if (me.hasListeners.resize && (width !== oldWidth || height !== oldHeight)) {
            me.fireEvent('resize', me, width, height, oldWidth, oldHeight);
        }
    },
    //布局前执行的模板方法,返回false禁止布局
    beforeComponentLayout: function(width, height) {
        return true;
    },
    //设置组件的位置
    setPosition : function(x, y, animate) {
        var me = this,
            pos = me.beforeSetPosition.apply(me, arguments);//设置位置前模板方法
        if (pos && me.rendered) {
            pos = me.convertPosition(pos);
            if (pos.left !== me.el.getLeft() || pos.top !== me.el.getTop()) {
                if (animate) { //可选的动画
                    me.stopAnimation();
                    me.animate(Ext.apply({
                        duration: 1000,
                        listeners: {
                            afteranimate: Ext.Function.bind(me.afterSetPosition, me, [pos.left, pos.top])
                        },
                        to: pos
                    }, animate));
                } else {
                    if (pos.left !== undefined && pos.top !== undefined) {
                        me.el.setLeftTop(pos.left, pos.top);
                    } else if (pos.left !== undefined) {
                        me.el.setLeft(pos.left);
                    } else if (pos.top !==undefined) {
                        me.el.setTop(pos.top);
                    }
                    me.afterSetPosition(pos.left, pos.top);
                }
            }
        }
        return me;
    },
    //设置位置前模板
    beforeSetPosition: function (x, y, animate) {
        var pos, x0;
        if (!x || Ext.isNumber(x)) {
            pos = { x: x, y : y, anim: animate };
        } else if (Ext.isNumber(x0 = x[0])) {
            pos = { x : x0, y : x[1], anim: y };
        } else {
            pos = { x: x.x, y: x.y, anim: y };
        }
        pos.hasX = Ext.isNumber(pos.x);
        pos.hasY = Ext.isNumber(pos.y);
        this.x = pos.x;
        this.y = pos.y;
        return (pos.hasX || pos.hasY) ? pos : null;
    },
    //设置位置后模板
    //缺省实现:如果支持,触发move事件
    afterSetPosition: function(x, y) {
        var me = this;
        me.onPosition(x, y);
        if (me.hasListeners.move) {
            me.fireEvent('move', me, x, y);
        }
    },
    //组件位置变化后执行的模板方法
    onPosition: Ext.emptyFn,
    //设置宽高,就是在el元素上进行操作
    setWidth : function(width) {
        return this.setSize(width);
    },
    setHeight : function(height) {
        return this.setSize(undefined, height);
    },
    getSize : function() {
        return this.el.getSize();
    },
    getWidth : function() {
        return this.el.getWidth();
    },
    getHeight : function() {
        return this.el.getHeight();
    },
    //设置当前组件在父容器的停靠位置
    setDocked : function(dock, layoutParent) {
        var me = this;
 
        me.dock = dock;
        if (layoutParent && me.ownerCt && me.rendered) {
            me.ownerCt.updateLayout();//导致更新父容器的布局
        }
        return me;
    },
    //生命周期模板方法:销毁后执行
    //缺省实现
    onDestroy : function() {
        var me = this;
        if (me.monitorResize && Ext.EventManager.resizeEvent) {
            Ext.EventManager.resizeEvent.removeListener(me.setSize, me);
        }
        //销毁组件布局、遮罩、浮动后代
        Ext.destroy(
            me.componentLayout,
            me.loadMask,
            me.floatingDescendants
        );
    },
    //销毁组件
    destroy : function() {
        var me = this,
            selectors = me.renderSelectors,
            selector,
            el;
        if (!me.isDestroyed) {
            //如果beforedestroy事件处理返回false,则不销毁
            if (!me.hasListeners.beforedestroy || me.fireEvent('beforedestroy', me) !== false) {
                me.destroying = true;
                me.beforeDestroy();//销毁前模板
                //对于浮动组件
                if (me.floating) {
                    delete me.floatParent;//清除浮动父组件引用
                    //从ZIndexManager移除自己
                    if (me.zIndexManager) {
                        me.zIndexManager.unregister(me);
                    }
                } else if (me.ownerCt && me.ownerCt.remove) {
                    me.ownerCt.remove(me, false);
                }
                me.onDestroy();//销毁后模板
                Ext.destroy(me.plugins); //尝试销毁所有插件
                //发布销毁事件
                if (me.hasListeners.destroy) {
                    me.fireEvent('destroy', me);
                }
                //接触组件注册
                Ext.ComponentManager.unregister(me);
                //销毁组件状态
                me.mixins.state.destroy.call(me);
                //清除组件监听器
                me.clearListeners();
                //确保在移除所有事件监听器后,清除所有对DOM元素的引用,避免内存泄漏
                if (me.rendered) {//如果尚未渲染,任何DOM结构都未创建,无需处理
                    if (!me.preserveElOnDestroy) {
                        me.el.remove(); //删除组件的顶级元素
                    }
                    me.mixins.elementCt.destroy.call(me); //删除所有childEls
                    if (selectors) {//如果配置了renderSelectors
                        for (selector in selectors) {
                            if (selectors.hasOwnProperty(selector)) {
                                el = me[selector];
                                //对于所有作为成员变量的元素,需要解除关系
                                if (el) {
                                    delete me[selector];
                                    el.remove();
                                }
                            }
                        }
                    }
                    delete me.el; //删除顶级元素
                    delete me.frameBody; //删除组件体
                    delete me.rendered;
                }
                me.destroying = false;
                me.isDestroyed = true;
            }
        }
    }
}, /*当组件类被创建后,执行的回调,this指向新创建的类*/function() {
    var AbstractComponent = this;
    //方法别名
    AbstractComponent.createAlias({
        on: 'addListener',
        prev: 'previousSibling',
        next: 'nextSibling'
    });
    //静态方法快捷方式
    Ext.resumeLayouts = function (flush) {
        AbstractComponent.resumeLayouts(flush);
    };
    Ext.suspendLayouts = function () {
        AbstractComponent.suspendLayouts();
    };
 
    Ext.batchLayouts = function(fn, scope) {
        AbstractComponent.suspendLayouts();
        fn.call(scope);
        AbstractComponent.resumeLayouts(true);
    };
});
Ext.Component

该类是ExtJS的所有组件的基类,保护:

  1. 基本的隐藏/显示逻辑
  2. 基本的启用/禁用逻辑
  3. 组件尺寸控制行为

在ExtJS中,每个组件类都具有一个称为xtype的别名,可以用于组件的延迟创建。Component类提供了一下成员:

配置项/属性/方法  说明
{autoScroll} Boolean = false。如果设置为true,那么在组件布局元素上使用overflow : 'auto' 样式,这导致必要时出现滚动条;如果设置为false,会自动剪除溢出的内容,不显示滚动条。该配置不应该与overflowX、 overflowY一起使用
{columnWidth} Number/String,在Column布局中,定义当前组件的列宽度,可以设置为数字或者百分比
{region} String,在Border布局中用于指定该组件所在的位置:center,north,south,east,west被支持
{flex} Number,在HBox、VBox等布局中用于指定组件所占据的尺寸比例
{draggable} Boolean/Object = false,覆盖AbstractComponent。设置为true,可以让浮动组件可拖拽(其封装元素作为drag handle)。亦可提供一个传递给 ComponentDragger的配置以启动拖拽:
JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
Ext.create( 'Ext.Component', {
    constrain : true,
    floating : true,
    style : {
        backgroundColor : '#fff',
        border : '1px solid black'
    },
    html : '',
    draggable : {
        delegate : 'h1' //只有按在该元素上,组件才可以拖拽
    }
} ).show();
{resizable} Boolean/Object,如果设置为true,组件渲染后为其添加一个Resizer对象。亦可指定Resizer构造器需要的配置对象
{floating}

Boolean/Object = false,覆盖AbstractComponent。

如果指定为true,组件将脱离正常文档流,使用CSS绝对定位。Window、Menu等组件默认即是浮动的。通过编程方式手工render()的组件会在全局ZIndexManager(即Ext.WindowManager单例)中注册。

作为容器子组件的浮动组件

浮动组件会通过ownerCt链条来寻找ZIndexManager——依次上溯,直到找到一个本身是floating的父容器,浮动组件总是在它的浮动父容器上面显示,如果找不到floating的父容器,则使用全局ZIndexManager

如果配置为浮动组件,在渲染阶段会它寻找一个ZIndexManager来管理自己的z-index,以正确位置与其它浮动组件的层叠(Stack)关系。当浮动组件的toFront()方法被调用时,其ZIndexManager会把它放在最前面

浮动组件不参与容器的布局,因此它不会随着容器的渲染而渲染,需要手工调用show()才会进行渲染,在渲染后ownerCt属性被删除, floatParent属性被设置,执行其祖先floating容器或者undefined

{formBind} Boolean = false,如果组件位于FormPanel内,设置该配置为true导致组件的启用/禁用状态取决于表单的验证有效性
{overflowX} String = 'hidden',支持auto、scroll、hidden
{overflowY} String = 'hidden',支持auto、scroll、hidden
{resizeHandles} String = 'all',Resizer的handles配置项,仅 resizable = true时有意义
{toFrontOnShow} Boolean = true,如果设置为真,在浮动组件show()时,自动带到最前面
floatParent readonly Ext.Container,仅对浮动组件有意义,手工render()的浮动组件没有floatParent
zIndexManager readonly Ext.ZIndexManager,仅对浮动组件有意义,渲染后被设置
zIndexParent readonly Ext.Container,
bubble() Ext.Component ( Function fn, [Object scope], [Array args] )。向上(getBubbleTarget)传播执行一个方法,如果任何一次执行返回false,停止传播
cancelFocus() 取消任何在当前组件上的延迟聚集
cloneConfig() Ext.Component cloneConfig( Object overrides ) ,克隆当前组件,使用overrides 覆盖原来的配置项
focus() Ext.Component ( [Boolean selectText], [Boolean/Number delay] ) ,尝试聚焦当前组件,返回聚焦的组件。可选的延迟聚焦
getBox() Object ( [Boolean local] ),得到组件的度量对象,形式:{x, y, width, height}
getPosition() Number[] ( [Boolean local] ) ,返回组件的位置,如果local=true,返回的位置相对于元素的offsetParent
hide()

Ext.Component( [String/Ext.Element/Ext.Component animateTarget], [Function callback], [Object scope] ) 。隐藏组件,根据hideMode设置隐藏模式

scrollBy()

void ( Number/Number[]/Object deltaX, Number/Boolean/Object deltaY, Boolean/Object animate )

滚动组件的targetEl,可选动画

setLoading() Ext.LoadMask( Boolean/Object/String load, [Boolean targetEl] )。显示一个LoadMask。如果targetEl=true,则遮罩targetEl而不是el,对于面板,targetEl对应面板体
setOverflowXY() Ext.Component ( String overflowX, String overflowY )。在组件内容元素上设置overflow样式
setPagePosition() Ext.Component ( Number x, Number y, [Boolean/Object animate] )。设置组件的page XY 位置
show()

Ext.Component ( [String/Ext.Element animateTarget], [Function callback], [Object scope] )

显示当前组件,如果floating或者autoRender为true,则先render()。

在show()之后,浮动组件z-index被带到最前面

showAt()

void ( Number x, Number y, [Boolean/Object animate] )

在指定位置显示组件,浮动组件的x,y相对于其ownerCt(例如Menu),该方法经常用来显示上下文菜单

updateBox() Ext.Component ( Object box ),设置组件el的大小位置
组件查询方法  
findParentBy() Ext.container.Container ( Function fn )。根据过滤器函数查询第一个匹配的祖先容器
findParentByType() Ext.container.Container( String/Ext.Class xtype ) 。根据类型查询第一个匹配的祖先容器
模板方法  
afterHide() void ( [Function callback], [Object scope] )。当组件隐藏后调用
afterShow() void ( [String/Ext.Element animateTarget], [Function callback], [Object scope] )。在组件显示后调用
beforeShow() 在组件显示前调用
initComponent() 覆盖AbstractComponent版本。该方法是组件初始化的重要阶段,所有子类应当通过覆盖此方法来提供构造逻辑(一般不通过constructor方法),任何子类的覆盖版本必须调用父类的版本。下面是生成一个动态文字按钮的例子:
JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
Ext.define('DynamicButtonText', {
    extend: 'Ext.button.Button',
    initComponent: function() {
        //准备动态按钮
        this.text = new Date();
        this.renderTo = Ext.getBody();
        //调用父类构造器
        this.callParent();
    }
});
Ext.onReady(function() {
    Ext.create('DynamicButtonText');
});
onDestroy() 添加销毁逻辑,在调用覆盖版本的onDestroy后,当前组件被销毁
onHide()

void ( [String/Ext.Element/Ext.Component animateTarget], [Function callback], [Object scope] )

添加隐藏逻辑,在调用覆盖版本的onHide后,当前组件被隐藏

onShow()

void ( [String/Ext.Element animateTarget], [Function callback], [Object scope] )

添加显示逻辑,在调用覆盖版本的onShow后,当前组件显示

onShowComplete()

void ( [Function callback], [Object scope] )

在afterShow()之后调用

Ext.Component的代码分析如下:

Ext.Component.js
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
Ext.define('Ext.Component', {
    alias: ['widget.component', 'widget.box'],
    extend: 'Ext.AbstractComponent',
    statics: {
        DIRECTION_TOP: 'top',
        DIRECTION_RIGHT: 'right',
        DIRECTION_BOTTOM: 'bottom',
        DIRECTION_LEFT: 'left',
        VERTICAL_DIRECTION_Re: /^(?:top|bottom)$/,
        INVALID_ID_CHARS_Re: /[\.,\s]/g
    },
    resizeHandles: 'all',
    floating: false,
    toFrontOnShow: true,
    hideMode: 'display',
    bubbleEvents: [],
    monPropRe: /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/,
    
    constructor: function(config) {
        var me = this;
        config = config || {};
        if (config.initialConfig) {
            if (config.isAction) {
                //从Ext.Action初始化
                me.baseAction = config;
            }
            config = config.initialConfig;
        }
        else if (config.tagName || config.dom || Ext.isString(config)) {
            config = {
                applyTo: config,
                id: config.id || config
            };
        }
        me.callParent([config]);//调用覆盖版本
        if (me.baseAction){
            me.baseAction.addComponent(me);
        }
    },
    initComponent: function() {
        var me = this;
        me.callParent();
        if (me.listeners) {
            me.on(me.listeners);//添加监听器
            me.listeners = null;//移除配置项
        }
        me.enableBubble(me.bubbleEvents); //启用事件冒泡
        me.mons = [];
    },
    afterRender: function() {
        var me = this;
        me.callParent();
        //设置页面位置
        if (!(me.x && me.y) && (me.pageX || me.pageY)) {
            me.setPagePosition(me.pageX, me.pageY);
        }
    },
    beforeRender: function () {
        var me = this,
            floating = me.floating,
            cls;
        if (floating) {
            //处理浮动组件
            me.addCls(Ext.baseCSSPrefix + 'layer');
            cls = floating.cls;
            if (cls) {
                me.addCls(cls);
            }
        }
        return me.callParent(); //调用覆盖版本
    },
    //布局后模板
    afterComponentLayout: function(){
        this.callParent(arguments);
        if (this.floating) {
            //处理浮动组件
            this.onAfterFloatLayout();
        }
    },
    //构造浮动混入
    makeFloating : function (dom) {
        this.mixins.floating.constructor.call(this, dom);
    },
    wrapPrimaryEl: function (dom) {
        if (this.floating) {
            this.makeFloating(dom);
        } else {
            this.callParent(arguments);
        }
    },
    //初始化尺寸调整器
    initResizable: function(resizable) {
        var me = this;
        resizable = Ext.apply({
            target: me,
            dynamic: false,
            constrainTo: me.constrainTo || (me.floatParent ? me.floatParent.getTargetEl() : null),
            handles: me.resizeHandles
        }, resizable);
        resizable.target = me;
        me.resizer = new Ext.resizer.Resizer(resizable);
    },
    getDragEl: function() {
        return this.el; //被拖拽的元素
    },
    //初始化拖拽
    initDraggable: function() {
        var me = this,
            //如果拖拽目标不是封装元素本身,则从resizer.el创建一个简单组件作为拖拽目标
            dragTarget = (me.resizer && me.resizer.el !== me.el) ? me.resizerComponent = new Ext.Component({
                el: me.resizer.el,
                rendered: true,
                container: me.container
            }) : me,
            ddConfig = Ext.applyIf({
                el: dragTarget.getDragEl(),
                //拖拽限制范围
                constrainTo: me.constrain ? (me.constrainTo || (me.floatParent ? me.floatParent.getTargetEl() : me.el.getScopeParent())) : undefined
            }, me.draggable);
        if (me.constrain || me.constrainDelegate) {
            ddConfig.constrain = me.constrain;
            ddConfig.constrainDelegate = me.constrainDelegate;
        }
        //设置dd,即组件拖拽器(处理任何组件的drag行为的对象)
        me.dd = new Ext.util.ComponentDragger(dragTarget, ddConfig);
    },
    //滚动组件的targetEl
    scrollBy: function(deltaX, deltaY, animate) {
        var el;
        if ((el = this.getTargetEl()) && el.dom) {
            el.scrollBy.apply(el, arguments);
        }
    },
    //设置组件位置前的模板
    beforeSetPosition: function () {
        var me = this,
            pos = me.callParent(arguments),
            adj;
 
        if (pos) {
            adj = me.adjustPosition(pos.x, pos.y);
            pos.x = adj.x;
            pos.y = adj.y;
        }
        return pos || null;
    },
    //设置组件位置后的模板
    afterSetPosition: function(ax, ay) {
        this.onPosition(ax, ay);
        this.fireEvent('move', this, ax, ay);
    },
    //在指定位置显示组件
    showAt: function(x, y, animate) {
        var me = this;
 
        if (!me.rendered && (me.autoRender || me.floating)) {
            me.doAutoRender(); //如果尚未渲染,执行渲染
            me.hidden = true; //设置hidden状态,以便后续发布beforeshow、show事件
        }
        if (me.floating) {
            //对于浮动组件,设置位置
            me.setPosition(x, y, animate);
        } else {
            //否则,设置Page位置
            me.setPagePosition(x, y, animate);
        }
        me.show();
    },
    setPagePosition: function(x, y, animate) {
        var me = this,
            p,
            floatParentBox;
 
        if (Ext.isArray(x)) {
            y = x[1];
            x = x[0];
        }
        me.pageX = x;
        me.pageY = y;
 
        if (me.floating) {
            if (me.isContainedFloater()) {
                //在容器中注册的浮动组件,必须把x,y设置为相对于容器的值
                floatParentBox = me.floatParent.getTargetEl().getViewRegion();
                if (Ext.isNumber(x) && Ext.isNumber(floatParentBox.left)) {
                    x -= floatParentBox.left;
                }
                if (Ext.isNumber(y) && Ext.isNumber(floatParentBox.top)) {
                    y -= floatParentBox.top;
                }
            } else {
                p = me.el.translatePoints(x, y); //将文档坐标转换为页面坐标
                x = p.left;
                y = p.top;
            }
 
            me.setPosition(x, y, animate);
        } else {
            p = me.el.translatePoints(x, y);    //将文档坐标转换为页面坐标
            me.setPosition(p.left, p.top, animate);
        }
        return me;
    },
    //得到组件的位置
    getPosition: function(local) {
        var me = this,
            el = me.el,
            xy,
            isContainedFloater = me.isContainedFloater(),
            floatParentBox;
 
        //对于非浮动组件,本地位置就是相对于el的offsetParent的位置偏移量
        if ((local === true) && !isContainedFloater) {
            return [el.getLocalX(), el.getLocalY()];
        }
        xy = me.el.getXY();
        //对于浮动组件,本地位置就是相对于容器的targetEl的位置偏移量
        if ((local === true) && isContainedFloater) {
            floatParentBox = me.floatParent.getTargetEl().getViewRegion();
            xy[0] -= floatParentBox.left;
            xy[1] -= floatParentBox.top;
        }
        return xy;
    },
    //显示组件
    show: function(animateTarget, cb, scope) {
        var me = this,
            rendered = me.rendered;
 
        if (rendered && me.isVisible()) {
            if (me.toFrontOnShow && me.floating) {
                //如果已渲染并且是可见的,带到最前面
                me.toFront();
            }
        } else {
            //触发beforeshow,如果返回false,什么都不做
            if (me.fireEvent('beforeshow', me) !== false) {
                me.hidden = false;
                if (!rendered && (me.autoRender || me.floating)) {
                    //如果尚未渲染,执行渲染
                    me.doAutoRender();
                    rendered = me.rendered;
                }
                if (rendered) {
                    //如果渲染成功
                    me.beforeShow();  //beforeShow模板
                    me.onShow.apply(me, arguments);//onShow模板
                    me.afterShow.apply(me, arguments);//afterShow模板
                }
            } else {
                me.onShowVeto();
            }
        }
        return me;
    },
    onShowVeto: Ext.emptyFn,
    //生命周期模板方法:显示前,渲染后
    beforeShow: Ext.emptyFn,
    //生命周期模板方法:显示时
    onShow: function() {
        var me = this;
        me.el.show(); //底层HTML元素的显示
        me.callParent(arguments);
        if (me.floating) {
            //处理浮动组件的尺寸
            if (me.maximized) {
                me.fitContainer();
            }
            else if (me.constrain) {
                me.doConstrain();
            }
        }
    },
    //生命周期模板方法:显示后
    afterShow: function(animateTarget, cb, scope) {
        var me = this,
            fromBox,
            toBox,
            ghostPanel;
        animateTarget = animateTarget || me.animateTarget;
        if (!me.ghost) {
            animateTarget = null;
        }
        if (animateTarget) {
            //处理动画
            animateTarget = animateTarget.el ? animateTarget.el : Ext.get(animateTarget);
            toBox = me.el.getBox();
            fromBox = animateTarget.getBox();
            me.el.addCls(Ext.baseCSSPrefix + 'hide-offsets');
            ghostPanel = me.ghost();
            ghostPanel.el.stopAnimation();
            ghostPanel.el.setX(-10000);
            ghostPanel.el.animate({
                from: fromBox,
                to: toBox,
                listeners: {
                    afteranimate: function() {
                        delete ghostPanel.componentLayout.lastComponentSize;
                        me.unghost();
                        me.el.removeCls(Ext.baseCSSPrefix + 'hide-offsets');
                        //动画完毕后,继续调用下一模板
                        me.onShowComplete(cb, scope);
                    }
                }
            });
        }
        else {
            //直接调用下一模板
            me.onShowComplete(cb, scope);
        }
    },
    //生命周期模板方法:显示(包括动画效果)完成
    onShowComplete: function(cb, scope) {
        var me = this;
        if (me.floating) {
            me.toFront();
            me.onFloatShow();
        }
        Ext.callback(cb, scope || me);
        me.fireEvent('show', me);  //发布show事件
        delete me.hiddenByLayout;
    },
    //隐藏组件
    hide: function() {
        var me = this;
        me.showOnParentShow = false;
        //触发beforehide事件,如果false什么都不做
        if (!(me.rendered && !me.isVisible()) && me.fireEvent('beforehide', me) !== false) {
            me.hidden = true;
            if (me.rendered) {
                //如果已经渲染过,则调用onHide模板
                me.onHide.apply(me, arguments);
            }
        }
        return me;
    },
    //生命周期模板方法:隐藏
    onHide: function(animateTarget, cb, scope) {
        var me = this,
            ghostPanel,
            toBox;
        animateTarget = animateTarget || me.animateTarget;
        if (!me.ghost) {
            animateTarget = null;
        }
        if (animateTarget) {
            //处理动画
            animateTarget = animateTarget.el ? animateTarget.el : Ext.get(animateTarget);
            ghostPanel = me.ghost();
            ghostPanel.el.stopAnimation();
            toBox = animateTarget.getBox();
            toBox.width += 'px';
            toBox.height += 'px';
            ghostPanel.el.animate({
                to: toBox,
                listeners: {
                    afteranimate: function() {
                        delete ghostPanel.componentLayout.lastComponentSize;
                        ghostPanel.el.hide();
                        me.afterHide(cb, scope);//动画完毕后,继续调用下一模板
                    }
                }
            });
        }
        me.el.hide(); //隐藏组件的元素
        if (!animateTarget) {
            me.afterHide(cb, scope);//直接调用下一模板
        }
    },
    //声明周期模板方法:隐藏后
    afterHide: function(cb, scope) {
        var me = this;
        delete me.hiddenByLayout;
        Ext.AbstractComponent.prototype.onHide.call(this);
        Ext.callback(cb, scope || me);
        me.fireEvent('hide', me); //发布hide事件
    },
    //声明周期模板方法:销毁时
    onDestroy: function() {
        var me = this;
        if (me.rendered) {
            //如果已经渲染,则销毁组件附加的对象
            Ext.destroy(
                me.proxy,
                me.proxyWrap,
                me.resizer,
                me.resizerComponent
            );
        }
        delete me.focusTask; //删除聚焦任务
        me.callParent();
    },
    //处理组件聚焦
    focus: function(selectText, delay) {
        var me = this,
            focusEl,
            focusElDom,
            containerScrollTop;
        if (delay) {
            //延迟聚焦
            if (!me.focusTask) {
                me.focusTask = new Ext.util.DelayedTask(me.focus);
            }
            me.focusTask.delay(Ext.isNumber(delay) ? delay : 10, null, me, [selectText, false]);
            return me;
        }
        if (me.rendered && !me.isDestroyed && me.isVisible(true) && (focusEl = me.getFocusEl())) {
            if (focusEl.isComponent) {
                //聚焦行为代理给子代组件
                return focusEl.focus(selectText, delay);
            }
            if ((focusElDom = focusEl.dom)) {
                if (focusEl.needsTabIndex()) {
                    focusElDom.tabIndex = -1; //设置tabIndex
                }
                if (me.floating) {
                    containerScrollTop = me.container.dom.scrollTop;
                }
                //直接使用原生HTML聚焦
                focusEl.focus();
                if (selectText === true) {
                    focusElDom.select();
                }
            }
            if (me.floating) {
                //对于浮动组件,聚集意味着位于最前
                me.toFront(true);
                if (containerScrollTop !== undefined) {
                    me.container.dom.scrollTop = containerScrollTop;
                }
            }
        }
        return me;
    },
    //取消延迟的聚焦
    cancelFocus: function() {
        var task = this.focusTask;
        if (task) {
            task.cancel();
        }
    },
    //失焦处理
    blur: function() {
        var focusEl;
        if (this.rendered && (focusEl = this.getFocusEl())) {
            focusEl.blur();
        }
        return this;
    },
    //当大小改变时执行的模板方法
    onResize: Ext.emptyFn,
    //获取事件传播的目标
    getBubbleTarget: function() {
        return this.ownerCt || this.floatParent;
    },
    //得到“内容目标”
    getContentTarget: function() {
        return this.el;
    },
    //克隆组件
    cloneConfig: function(overrides) {
        overrides = overrides || {};
        var id = overrides.id || Ext.id(),
            cfg = Ext.applyIf(overrides, this.initialConfig),//获取当前组件的初始配置
            self;
 
        cfg.id = id;
 
        self = Ext.getClass(this); //获取当前组件的类型
        return new self(cfg); //调用构造器
    },
    //向上传播的执行一函数
    bubble: function(fn, scope, args) {
        var p = this;
        while (p) {
            if (fn.apply(scope || p, args || [p]) === false) {
                break;
            }
            p = p.getBubbleTarget();
        }
        return this;
    }
});
← ExtJS 4的事件系统
ExtJS 4的容器机制 →

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">

Related Posts

  • ExtJS 4元素与组件查询
  • 浅析ExtJS 4布局组件
  • ExtJS 4组件的生命周期
  • Sencha Cmd学习笔记
  • ExtJS 4常用组件之表格

Recent Posts

  • Investigating and Solving the Issue of Failed Certificate Request with ZeroSSL and Cert-Manager
  • A Comprehensive Study of Kotlin for Java Developers
  • 背诵营笔记
  • 利用LangChain和语言模型交互
  • 享学营笔记
ABOUT ME

汪震 | Alex Wong

江苏淮安人,现居北京。目前供职于腾讯云,专注容器方向。

GitHub:gmemcc

Git:git.gmem.cc

Email:gmemjunk@gmem.cc@me.com

ABOUT GMEM

绿色记忆是我的个人网站,域名gmem.cc中G是Green的简写,MEM是Memory的简写,CC则是我的小天使彩彩名字的简写。

我在这里记录自己的工作与生活,同时和大家分享一些编程方面的知识。

GMEM HISTORY
v2.00:微风
v1.03:单车旅行
v1.02:夏日版
v1.01:未完成
v0.10:彩虹天堂
v0.01:阳光海岸
MIRROR INFO
Meta
  • Log in
  • Entries RSS
  • Comments RSS
  • WordPress.org
Recent Posts
  • Investigating and Solving the Issue of Failed Certificate Request with ZeroSSL and Cert-Manager
    In this blog post, I will walk ...
  • A Comprehensive Study of Kotlin for Java Developers
    Introduction Purpose of the Study Understanding the Mo ...
  • 背诵营笔记
    Day 1 Find Your Greatness 原文 Greatness. It’s just ...
  • 利用LangChain和语言模型交互
    LangChain是什么 从名字上可以看出来,LangChain可以用来构建自然语言处理能力的链条。它是一个库 ...
  • 享学营笔记
    Unit 1 At home Lesson 1 In the ...
  • K8S集群跨云迁移
    要将K8S集群从一个云服务商迁移到另外一个,需要解决以下问题: 各种K8S资源的迁移 工作负载所挂载的数 ...
  • Terraform快速参考
    简介 Terraform用于实现基础设施即代码(infrastructure as code)—— 通过代码( ...
  • 草缸2021
    经过四个多月的努力,我的小小荷兰景到达极致了状态。

  • 编写Kubernetes风格的APIServer
    背景 前段时间接到一个需求做一个工具,工具将在K8S中运行。需求很适合用控制器模式实现,很自然的就基于kube ...
  • 记录一次KeyDB缓慢的定位过程
    环境说明 运行环境 这个问题出现在一套搭建在虚拟机上的Kubernetes 1.18集群上。集群有三个节点: ...
  • eBPF学习笔记
    简介 BPF,即Berkeley Packet Filter,是一个古老的网络封包过滤机制。它允许从用户空间注 ...
  • IPVS模式下ClusterIP泄露宿主机端口的问题
    问题 在一个启用了IPVS模式kube-proxy的K8S集群中,运行着一个Docker Registry服务 ...
  • 念爷爷
      今天是爷爷的头七,十二月七日、阴历十月廿三中午,老人家与世长辞。   九月初,回家看望刚动完手术的爸爸,发

  • 6 杨梅坑

  • liuhuashan
    深圳人才公园的网红景点 —— 流花山

  • 1 2020年10月拈花湾

  • 内核缺陷触发的NodePort服务63秒延迟问题
    现象 我们有一个新创建的TKE 1.3.0集群,使用基于Galaxy + Flannel(VXLAN模式)的容 ...
  • Galaxy学习笔记
    简介 Galaxy是TKEStack的一个网络组件,支持为TKE集群提供Overlay/Underlay容器网 ...
TOPLINKS
  • Zitahli's blue 91 people like this
  • 梦中的婚礼 64 people like this
  • 汪静好 61 people like this
  • 那年我一岁 36 people like this
  • 为了爱 28 people like this
  • 小绿彩 26 people like this
  • 彩虹姐姐的笑脸 24 people like this
  • 杨梅坑 6 people like this
  • 亚龙湾之旅 1 people like this
  • 汪昌博 people like this
  • 2013年11月香山 10 people like this
  • 2013年7月秦皇岛 6 people like this
  • 2013年6月蓟县盘山 5 people like this
  • 2013年2月梅花山 2 people like this
  • 2013年淮阴自贡迎春灯会 3 people like this
  • 2012年镇江金山游 1 people like this
  • 2012年徽杭古道 9 people like this
  • 2011年清明节后扬州行 1 people like this
  • 2008年十一云龙公园 5 people like this
  • 2008年之秋忆 7 people like this
  • 老照片 13 people like this
  • 火一样的六月 16 people like this
  • 发黄的相片 3 people like this
  • Cesium学习笔记 90 people like this
  • IntelliJ IDEA知识集锦 59 people like this
  • 基于Kurento搭建WebRTC服务器 38 people like this
  • Bazel学习笔记 37 people like this
  • PhoneGap学习笔记 32 people like this
  • NaCl学习笔记 32 people like this
  • 使用Oracle Java Mission Control监控JVM运行状态 29 people like this
  • Ceph学习笔记 27 people like this
  • 基于Calico的CNI 27 people like this
Tag Cloud
ActiveMQ AspectJ CDT Ceph Chrome CNI Command Cordova Coroutine CXF Cygwin DNS Docker eBPF Eclipse ExtJS F7 FAQ Groovy Hibernate HTTP IntelliJ IO编程 IPVS JacksonJSON JMS JSON JVM K8S kernel LB libvirt Linux知识 Linux编程 LOG Maven MinGW Mock Monitoring Multimedia MVC MySQL netfs Netty Nginx NIO Node.js NoSQL Oracle PDT PHP Redis RPC Scheduler ServiceMesh SNMP Spring SSL svn Tomcat TSDB Ubuntu WebGL WebRTC WebService WebSocket wxWidgets XDebug XML XPath XRM ZooKeeper 亚龙湾 单元测试 学习笔记 实时处理 并发编程 彩姐 性能剖析 性能调优 文本处理 新特性 架构模式 系统编程 网络编程 视频监控 设计模式 远程调试 配置文件 齐塔莉
Recent Comments
  • qg on Istio中的透明代理问题
  • heao on 基于本地gRPC的Go插件系统
  • 黄豆豆 on Ginkgo学习笔记
  • cloud on OpenStack学习笔记
  • 5dragoncon on Cilium学习笔记
  • Archeb on 重温iptables
  • C/C++编程:WebSocketpp(Linux + Clion + boostAsio) – 源码巴士 on 基于C/C++的WebSocket库
  • jerbin on eBPF学习笔记
  • point on Istio中的透明代理问题
  • G on Istio中的透明代理问题
  • 绿色记忆:Go语言单元测试和仿冒 on Ginkgo学习笔记
  • point on Istio中的透明代理问题
  • 【Maven】maven插件开发实战 – IT汇 on Maven插件开发
  • chenlx on eBPF学习笔记
  • Alex on eBPF学习笔记
  • CFC4N on eBPF学习笔记
  • 李运田 on 念爷爷
  • yongman on 记录一次KeyDB缓慢的定位过程
  • Alex on Istio中的透明代理问题
  • will on Istio中的透明代理问题
  • will on Istio中的透明代理问题
  • haolipeng on 基于本地gRPC的Go插件系统
  • 吴杰 on 基于C/C++的WebSocket库
©2005-2025 Gmem.cc | Powered by WordPress | 京ICP备18007345号-2