Extjs容器的渲染机制

日期:2015-08-25点击次数:16715

       在ExtJS中,容器是一种特殊的组件,容器与一般组件的根本差别在于,它的内部可以包含其他组件(包括容器)作为其“子组件(items)”。容器提供添加、删除、插入子组件的方法和相关的事件。此外,容器还引入了“容器布局”,专门处理子组件的大小、位置。

       考虑下面的例子:
Ext.create( 'Ext.container.Container', {
    renderTo : Ext.getBody(),
    id : 'c0',
    width : 250,
    height : 250,
    style : {
        backgroundColor : '#f88'
    },
    html:'This is a container',
    items : [
        {
            id : 'c0-b0',
            xtype : 'box',
            width : 100,
            height : 100,
            style : {
                backgroundColor : '#8f8'
            }
        }, {
            id : 'c0-b1',
            xtype : 'box',
            width : 100,
            height : 100,
            style : {
                backgroundColor : '#88f'
            }
        }
    ]
} );
       这是包含两个Component(b0,b1)的Container(c0),使用Auto布局。下面是最终渲染效果和DOM结构:

       在容器c0的初始化(主要是initComponent)阶段,包含以下重要逻辑:
1.      调用getLayout()初始化布局对象
       1.      使用静态函数Ext.layout.Layout.create()创建布局对象,由于未指定布局方式,创建为Ext.layout.container.Auto
       2.      调用AbstractContainer.setLayout()设置容器与布局的关联性。注意布局和容器是一对一的关联关系
2.      调用initItems()初始化子组件
       1.      initItems()调用AbstractContainer.add()
       2.      add()调用prepareItems(),把b0、b1添加为子组件,并完成子组件的初始化阶段:
                1.      调用lookupComponent()查找组件:如果子组件存在于ComponentManager中,则获取之,否则构造之
                2.      在本例中需要构造,调用子组件的constructor、initComponent
                         1.      如果子组件也是容器(本例不是),则又是一个递归的过程,直到最底层的子代组件被初始化
       3.      add()遍历所有被初始化完毕的子组件,执行:
                1.      对于浮动组件,转移到floatingItems集合中,并调用子组件onAdded()模板
                2.      对于非浮动组件,存放在items集合中,分别:
                         1.      触发beforeadd事件、调用onBeforeAdd模板,只要任一返回false,取消添加
                         2.      调用子组件onAdded(),导致子组件added事件触发
                         3.      调用容器onAdd()
                         4.      调用布局onAdd()
                         5.      发布add事件
3.      由于配置了renderTo,触发渲染阶段开始
       渲染阶段会自上而下的onRender,并自下而上的完成afterRender,完成整个组件层次的渲染。渲染的核心是渲染树,渲染树包括tpl属性,是一个XTemplate模板,以及从Layout等对象获取过来控制渲染细节的若干函数。
       渲染树tpl.html定义了模板内容,在填充渲染树模板过程中,通过调用renderContainer(),容器的渲染行为会最终委托给布局对象,而布局也是使用模板机制进行渲染。
       renderContainer()方法是容器渲染(即生成DOM)结构的核心,其逻辑包括渲染树的生成、渲染树插入DOM两个部分。
       渲染树生成的详细步骤如下:
1.      如果容器使用模拟的圆角外框(注意,如果支持CSS3则绝不会通过图片去模拟圆角外框效果),调用initFramingTpl()
        1.      获取Renderable.frameTpl或者frameTplTable
        2.      调用Renderable.setupFramingTpl()对frameTpl进行预处理:
                 1.      把模板成员函数applyRenderTpl关联到this.doApplyRenderTpl
                 2.      把模板成员函数renderDockedItems关联到this.doRenderFramingDockedItems
        3.      frameTpl中包含对applyRenderTpl()的调用代码,这段代码的前后则是圆角外框效果的HTML
        4.      applyRenderTpl()就是doApplyRenderTpl(),后者则转调initRenderTpl()
        5.      至此,可以看到initRenderTpl()是核心所在
2.      如果容器不使用模拟的圆角外框,直接调用initRenderTpl()
        1.      调用getTpl()把上表中AbstractContainer.renderTpl获取作为模板
        2.      调用setupRenderTpl()
                 1.      获取容器布局对象
                 2.      调用Renderable覆盖版本:设置tpl.renderBody=tpl.renderContent=this.doRenderContent
                 3.      调用layout.setupRenderTpl
                          1.      tpl.renderBody = layout.doRenderBody
                          2.      tpl.renderContainer = layout.doRenderContainer
                          3.      tpl.renderItems = layout.doRenderItems
                          4.      tpl.renderPadder = layout.doRenderPadder
       渲染树插入DOM的具体过程如下:
1.      获取c0的渲染树后,Renderable.render()调用Ext.DomHelper.append()进行DOM插入
2.      调用 Ext.DomHelper.insert() 
3.      调用Ext.DomHelper.markup(),调用generateMarkup()生成渲染树的HTML字符串,具体如下:
       1.      输出c0的封装元素:<tree.tag,即 <div 
       2.      遍历tree的属性,判断哪些需要作为封装元素的属性
                1.      cls作为属性,输出: class="x-container x-container-default" 
                2.      style作为属性,输出: style="background-color:#f88;width:250px;height:250px;"
                3.      id作为属性,输出: id="c0" 
       3.      输出 > ,关闭封装元素的开始标签
       4.      调用tree.tpl.applyOut,填充模板并输出:
                1.      调用renderContainer(),这一步是容器渲染核心的起点
                2.      上一步即调用Auto布局父类layout.container.Container.doRenderContainer()
                         1.      由于this指针的问题,从模板数据中取得$comp.layout,即当前布局对象
                         2.      获取布局的渲染模板lt,调用layout.getRenderTpl(),结果如上表Auto
                         3.      调用layout.owner.setupRenderTpl()对lt进行预处理,owner即c0
                                  1.      设置lt.renderBody=lt.renderContent=c0.doRenderContent
                                  2.      调用layout.setupRenderTpl(),设置:
                                            1.      lt.renderBody = layout.doRenderBody
                                            2.      lt.renderContainer = layout.doRenderContainer
                                            3.      lt.renderItems = layout.doRenderItems
                                            4.      lt.renderPadder = layout.doRenderPadder
                         4.      以上3步完成布局渲染模板的初始化后,调用layout.getRenderData()初始化渲染上下文:
                                  1.      $comp = c0
                                  2.      $layout = c0.layout
                                  3.      ownerId = c.id
                         5.      执行布局渲染模板的applyOut()
                                  1.      调用lt.renderBody(),即layout.doRenderBody()
                                           1.      调用lt.renderItems(),即layout.doRenderItems()渲染子组件
                                           2.      调用lt.renderContent(),即c0.doRenderContent()渲染容器内容
                                  2.      输出: <div id="c0-clearEl" class="x-clear" role="presentation"></div>
       5.      输出c0的封装元素的关闭标签: </div> 
4.      Renderable.render()调用wrapPrimaryEl(),至此,c0.getEl()不再返回undefined
5.      调用Renderable.finishRender(),完成渲染过程
       至此,容器的渲染阶段完毕,后续将根据需要进行容器及其子组件的展示。

 


软件部         汪震

 

姓名:
性别:
电话:
E-mail
问题:
问题描述: