这是Jerry在2020年的第79篇文章,也是王子熙一共第261篇原创文章。系列目录(0)SAPUI5应用开发者理解UI5框架代码含义(1)UI5模块懒加载机制(2)UI5控件渲染机制(本文)(3)HTML原生事件VSSAPUI5语义事件(4)UI5控件元数据实现细节(五)UI5控件实例数据实现细节(六)UI5控件数据绑定实现原理(七)UI5控件数据绑定的三种模式:OneWay、TwoWay和OneTime实现原理对比(八)生成UI5控件ID的逻辑(九)UI5控件多语言(Internationalization,i18n)支持的实现原理(十)XML视图中的Button控件(十一)Button控件及其背后的DOM元素使用Jerry's文章为SAP的脚手架应用没有任何后台API依赖的UI5学习。创建一个仅包含按钮控件的SAPUI5应用程序。使用Chrome开发者工具中的Elements工具栏查看按钮控件的原生HTML代码:在Jerry的上一篇文章中,我们深入研究了SAPUI5框架代码系列之一:UI5Module的懒加载机制。我们了解到,UI5的ButtonModule之一,ButtonRenderer,负责将sap.ui.commons.button的实例数据渲染成原生HTML代码。在ButtonRenderer.render函数中设置断点,然后F5刷新页面,当断点被触发时,可以从调用栈中观察到RenderManager是如何调用ButtonRenderer进行渲染工作的。下图有点乱,意在表达最终渲染的HTML源码中的button标签的各个属性是由ButtonRenderer的哪一行代码实现的。Jerry在刚刚做SAPUI5开发的时候,在了解了Renderer机制后,心里有个疑问,SAPUI5是怎么知道按钮控件的renderer是ButtonRenderer的。换句话说,SAPUI5控件与其渲染器之间的一一对应关系是如何保持的?在SAPUI5框架中,每一种控件都维护一份Metadata(元数据)副本,其中有一个getRenderer方法,返回该控件对应的渲染器名称。关于SAPUI5控件元数据,本系列后续文章会介绍。从调试器可以观察到,在按钮控件的元数据中,变量_sRendererName维护着按钮渲染器的名称:sap.ui.commons.ButtonRenderer。然而,这个变量是什么时候赋值的呢?从下图中第42971行可以看出:控件的renderer符合命名约定:<控件名称>+“Renderer”,简单的字符串拼接操作。RenderManager什么时候开始重绘控件?我们稍微修改一下脚手架应用中的按钮点击事件处理函数:每次点击按钮,调用setText修改按钮的text属性:点击按钮,发现再次触发ButtonRenderer.render。原因是oButton1.setText最终会调用按钮原型链上的ManagedObject.setProperty方法,其中有一个显式invalidate调用。如果你忘记了SAPUI5控件的原型链设计,可以查看Jerry的上一篇文章:深入研究SAPUI5框架代码系列之一:UI5Module的懒加载机制。Control.invalidate内部计算后,会得到当前页面需要重绘的区域,最后调用RenderManager进行重绘。我们先简单看一下Angular中的控制图。以SAPSpartacus的ProductCarousel展示控件为例:有12款畅销产品,展示在多个屏幕的轮播控件中,每个屏幕展示若干个产品。使用控件提供的左右箭头在屏幕之间切换。转盘底部的小红点表示转盘当前显示的是哪一屏数据。SAPUI5也可以实现类似的复合控件,官方称之为CustomControl。Spartacus产品轮播控件的HTML代码用标签cx-product-carousel表示,内部复用了另一个自定义标签cx-carousel:当前显示在屏幕上的产品信息通过三个类为itemactive的div标签显示在cx-carousel标签中。这个自定义产品轮播控件是通过Spartacus中的Angular产品轮播组件实现的。在ProductCarouselComponent的布局实现中,将Component自身的属性items$和title$作为输入,传入另一个Componentcx-carousel,使其将属性值title$渲染为轮播的标题,而轮播的数据源来自于传入的属性items$。因为cx-carousel是一个可复用的控件,除了展示产品轮播,还可以用来展示其他类似实体的轮播展示,比如折扣轮播,促销活动轮播等等。因此,除了将items$和title$传递给cx-carousel外,还需要将布局逻辑告知后者,在轮播内部展示轮播的各个元素。所以下图第九行通过
