深入学习 SAP UI5 框架代码系列之四:SAP UI5 控件的元数据实现
在本系列的第二篇文章: 里,我们了解了什么是UI5控件的渲染器(Renderer), 以及如何从SAP UI5控件的元数据里,获得其对应渲染器的名称:sap.ui.commons.ButtonRenderer

本文我们将了解更多关于 SAP UI5 控件元数据的细节。虽然作为 SAP UI5 应用开发人员,我们日常工作中,最经常打交道的,是本系列下一篇文章要介绍的 SAP UI5 控件的实例数据。但是,了解了 SAP UI5 控件元数据的设计原理,有助于 SAP UI5 开发人员,理解我们本地使用 Visual Studio Code 或者浏览器里使用 WebIDE, SAP Business Application Studio 这种在线开发工具时, SAP UI5 代码自动补全的实现原理。
如何在运行时获取SAP UI5控件的元数据?
拿到控件实例后,调用其getMetadata()方法即可。
在下图第19行设置断点,拿到button控件实例oButton1, 调用其getMetadata方法,返回值就是button控件的元数据,如下图右边所示。
元数据里字段很多,这里只介绍对SAP UI5应用开发人员来说最重要的一部分字段:

_aAllPublicMethods
当我们在Chrome开发者工具的console面板里,输入oButton1之后,紧接着再敲一个"."(英文输入状态下的句号),就能看到该button实例能够调用的一系列公有方法。这些公有方法列表,维护在元数据里的数组_aAllPublicMethods.

这个数组的length为110,这意味着button实例总共可以调用110个公有方法。然而我们在Button.js里查看button的实现源代码,发现里面能够找到的方法数目远远小于110.
这是因为button控件实例能调用的110个公有方法,很多都来自其原型链继承关系上游的节点里实现的公有方法。
Button->Control->Element->ManagedObject->EventProvider->BaseObject
想判断一个方法是button本身所实现,还是从其原型链继承过来的,可以将方法名称传入函数oButton1.hasOwnProperty, 通过其返回的布尔值得知结果。
比如getText是button原型链上的一个方法,因此下面的JavaScript语句返回:false.


_aAllPublicMethods数组的填充逻辑:控件本身的公有方法和其原型链上级节点的公有方法的并集(下图高亮JavaScript代码里数组的concat操作)

_aPublicMethods
该数组内的元素是刚刚介绍过的_aAllPublicMethods的一个子集,定义在metadata的_publicMethods字段内。下图是UI5控件原型链上的节点,EventProvider的publicMethods:


_mAllAggregations
该对象维护了控件所有可用的Aggregation.
在面向对象编程领域,我们常用association, aggregation(聚合)和composition(组合)描述类与类,对象与对象之间的关联关系。
Aggregation描述的是has-a的语义,在UML图里通过空心菱形箭头表示,比如tooltip可以脱离button控件单独存在;Composition关系描述part-of,即部分和整体的语义,用实心菱形箭头表示。

下图是一个例子:button控件的tooltip以Aggregation的方式维护和存储在控件元数据当中,通过getTooltip和setTooltip进行读写访问。


关于SAP UI5 Aggregation的权威介绍,可以查看SAP UI5.
官网里给出的一个例子,TextList的元数据里定义了一个指向texts的aggregation, 关系为1:N.

_mProperties
该对象包含了button控件所有可用的属性,以及针对这些属性的读写方法。注意mProperties存储的只是控件属性的元数据,而非属性运行时的值。SAP UI5控件运行时属性值的存储,会在本系列下一篇文章 UI5控件的实例数据实现细节 里介绍。

_oParent
指向该控件原型链父节点的元数据存储结构。顺着节点的oParent字段,我们可以遍历完整个原型链。

SAP UI5 button控件的公有方法,从Jerry 2015年写作时的110个,增添到了2021年1.85.0版本里的127个:

我们有两种方式可以手动计算出127这个数字。
方法1:button元数据的_aPublicMethods.length + _oParent._aAllPublicMethods.length

方法2:button自身,及其原型链上每个节点元数据的_aPublicMethods.length求和:

Jerry最近用Angular开发SAP Spartacus时,也会在Angular Component的template文件里使用控件。
这些控件的实现分为三类:
(1) 直接利用HTML原生标签,例如div, span, label, input这些:

(2) 由前缀为cx-的选择器代表的复合控件,这是我们团队开发的能够被SAP Spartacus其他UI和二次开发人员重用的控件,cx即Customer Experience.
比如下列高亮代码的语义是,如果当前Component的orderApproval模型处于可用状态,且loading标志位为true,则显示一个旋转的动画效果,即加载第四行的复合控件cx-spinner, 否则加载id为approvalFormTemplate的页面模板。复合控件本质上仍然是一个Angular Component:

对于以上这两种控件实现,不存在类似SAP UI5那样的源代码级别的元数据数据结构。
(3) 第三方提供的控件库,比如ng-select, 一个下拉菜单控件库:


在node_modules文件夹下的ng-select.component.d.ts文件里,即可找到类似SAP UI5控件的元数据定义:

本系列下一篇文章,我会介绍 SAP UI5 控件实例在运行时的属性值存储和读取实现,感谢阅读。