深入学习 SAP UI5 框架代码系列之二:UI5 控件的渲染器
![深入学习 SAP UI5 框架代码系列之二:UI5 控件的渲染器](https://static001.geekbang.org/infoq/af/af71649bc39e34473eb4a99e0ca91f33.png)
在 Jerry 本系列的前一篇文章 深入学习 SAP UI5 框架代码系列之一:UI5 Module 的懒加载机制,我们已经了解到 UI5 Button Module 之一,ButtonRenderer,专门负责将 sap.ui.commons.button 的实例数据,渲染成原生的 HTML 代码。
为什么要引入控件渲染器这个中间层呢?
我们看看 SAP UI5 应用一个典型的 xml 视图的源代码:
不难发现,源代码中位于命名控件 sap.m 下面的控件标签,如 App, pages, Page, Panel 这些例子,都并非 HTML 标准控件,因此无法被浏览器识别,必须要有一个中间层,将其转换成浏览器可以支持的 HTML 原生标签才行。
在 ButtonRenderer.render 函数里设置断点,然后 F5 刷新页面,断点触发,就可从调用栈观察到 RenderManager 是如何调用 ButtonRenderer 执行渲染工作的。
![](https://static001.geekbang.org/infoq/6c/6ce5bb1cc9e57a123c2ed3de609e8574.png)
下图画得有些乱,意图是想表达,最终渲染出的 HTML 源代码里的 button 标签的各个属性,分别是由 ButtonRenderer 哪一行代码实现的。
![](https://static001.geekbang.org/infoq/bc/bc34ebc90dcf209deeda70dfa67832de.png)
Jerry 刚刚做 SAP UI5 开发时,了解到 Renderer 机制后,心里有个疑问,SAP UI5 怎么知道 button 控件的渲染器是 ButtonRenderer。换言之,SAP UI5 控件和其渲染器之间的一一对应关系是如何维护的?
SAP UI5 框架里,每类控件都各自维护了一份 Metadata(元数据),其中有个 getRenderer 方法,返回控件对应的渲染器名称。
关于 SAP UI5 控件元数据,本系列后续文章会介绍。
![](https://static001.geekbang.org/infoq/10/107f487c202da430610949a45d95e046.png)
从调试器里能观察到 button 控件元数据里,变量_sRendererName 维护了 button 渲染器的名称:sap.ui.commons.ButtonRenderer. 然而,这个变量在何时赋的值?
![](https://static001.geekbang.org/infoq/15/153f65b6400a214d841f3ea17e7ad7e7.png)
从下图第 42971 行能够看出:控件的渲染器满足命名规范:<控件名称>+ "Renderer", 一个简单的字符串拼接操作。
![](https://static001.geekbang.org/infoq/8c/8c8191c0bdcaa5353b7dad3b0e35104c.png)
RenderManager 在哪些时刻会开启控件的重绘?
让我们对脚手架应用里的 button 点击事件处理函数稍作修改:每次点击按钮时,调用 setText 修改 button 的 text 属性:
![](https://static001.geekbang.org/infoq/45/45399f2150b772dd7ea3a70f298bc80e.png)
点击按钮,发现 ButtonRenderer.render 再次被触发。
![](https://static001.geekbang.org/infoq/e7/e77b68c5e6b8b60ec6287a480e15f248.png)
原因在于,oButton1.setText 最终会调用 button 原型链上的 ManagedObject.setProperty 方法,该方法内部有一个显式的 invalidate 调用。
如果忘记了 SAP UI5 控件的原型链设计,可以查看 Jerry 之前的文章:深入学习 SAP UI5 框架代码系列之一:UI5 Module 的懒加载机制。
![](https://static001.geekbang.org/infoq/46/4694da736da986cd779309212e1ed867.png)
Control.invalidate 内部经过计算,会得出当前页面需要重绘的区域,最终调用 RenderManager 进行重绘。
我们再来简单了解下 Angular 里的控件绘制。以 SAP Spartacus 的产品转盘(Product Carousel)显示控件为例: 最畅销的产品共有 12 款,分多屏显示在转盘控件里,每屏显示若干个产品。通过控件提供的左右箭头,进行屏与屏之间的切换。转盘底部的小红点,表示当前转盘显示的是第几个屏幕的数据。
![](https://static001.geekbang.org/infoq/1d/1d826ae3c4050444d7f730f0ddf04c75.png)
![](https://static001.geekbang.org/infoq/d1/d1fe8fcb5ff388de4f7cc0bedc870a59.png)
SAP UI5 也能实现类似的复合控件,官方称呼为 Custom Control.
Spartacus 产品转盘控件的 HTML 代码表现形式为标签 cx-product-carousel,内部重用了另一个自定义标签 cx-carousel:
![](https://static001.geekbang.org/infoq/c1/c1b225bdd6accc9fb6734e86ad0e1af8.png)
当前显示在屏幕里的产品信息,通过 cx-carousel 标签里三个 class 为 item active 的 div 标签显示。
![](https://static001.geekbang.org/infoq/19/198500d8d3384bc96be110393a2c4051.png)
这个自定义产品转盘控件通过 Spartacus 里的 Angular Product Carousel Component 实现。
Product Carousel Component 的 layout 实现里,将 Component 自身的属性 items作为输入,传入另一个 Component cx-carousel, 让其将属性值 title.
![](https://static001.geekbang.org/infoq/c5/c502a08627dc3ebf891974b9ed6c8dca.png)
因为 cx-carousel 是一个可重用控件,除了显示产品转盘外,还可以用于显示其他同类实体的转盘显示,比如折扣转盘,促销活动转盘等等。因此,除了将 items传入 cx-carousel 之外,还需要告知后者,在转盘内部,以何种布局逻辑显示转盘的每一个元素。
因此,下图第九行通过<ng-template>标签定义了一个 id 为 #carouselItem 的模板,将此 id 一并传入 cx-carousel. 这样,转盘控件在运行时,针对转盘数据源 items$内存储的每一个产品数据,就会按照此模板定义的布局,进行绘制。
![](https://static001.geekbang.org/infoq/bc/bcffc6da56371e1e696034c40eb3c2fd.png)
![](https://static001.geekbang.org/infoq/4b/4bdcb6e1ca341b2394e3ce926445f814.png)
当初 Jerry 学习 Spartacus 这个产品转盘的设计时,觉得很亲切,因为其设计思路和 SAP UI5 List Binding(Aggregation Binding)是一致的。
SAP UI5官网上讲解 List Binding 的一个例子:
有一个 companies JSON 数组:
![](https://static001.geekbang.org/infoq/a5/a5e81c608cfceec68088fb96ad217165.png)
将 companies 路径传入 List 控件,完成了数据源的指定,通知 List 去绘制 companies 数组里的数据。具体渲染哪些数据?List 不知道,需要 items 子控件来定义,比如子控件的 title 属性,显示 JSON 数组的 name 字段,description 属性,显示 JSON 数组的 city 字段。List 会根据 JSON 数组里的 company 节点的个数,动态创建对应数目的 items 子控件。
![](https://static001.geekbang.org/infoq/d8/d8eed7803cf5409efdffb4317b66c470.png)
这里的 SAP UI5 items 子控件,扮演的就是本文之前介绍的 Spartacus 产品转盘控件页面里,用<ng-template>定义出的 id 为 #carouselItem 的模板同样的角色。
感谢大家阅读。本系列下一篇文章:HTML 原生事件 VS SAP UI5 Semantic 事件,我会给大家分享,为什么 SAP UI5 要引入 Semantic 事件即语义事件的概念,而不直接使用 HTML 原生的事件。
版权声明: 本文为 InfoQ 作者【Jerry Wang】的原创文章。
原文链接:【http://xie.infoq.cn/article/8417523fade5ccda936c50d42】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论