这是Jerry 2021年的第6篇文章,也是汪子熙公众号总共第277篇原创文章。
系列目录
(0) SAP UI5应用开发人员了解UI5框架代码的意义
(1) SAP UI5 module懒加载机制
(2) SAP UI5 控件渲染机制
(3) HTML原生事件 VS SAP UI5 Semantic事件
(4) SAP UI5控件元数据的元数据实现
(5) SAP UI5控件的实例数据修改和读取逻辑(本文)
(6) SAP UI5控件数据绑定的实现原理
(7) SAP UI5控件数据绑定的三种模式:One Way,Two Way和OneTime实现原理比较
(8) SAP UI5控件ID的生成逻辑
(9) SAP UI5控件的多语言(国际化,Internationalization,i18n)支持的实现原理
(10) XML视图里的button控件
(11) button控件和它背后的DOM元素
本文我们将通过研究button控件的setText和getText方法实现,来学习SAP UI5控件的实例数据修改和读取逻辑。
下图是一段简单的SAP UI5代码:每点击一次button,就会在press事件的响应函数里,给button的text属性值尾部附上一个字符“1”.
点了三次按钮后,其渲染出的HTML代码如下图所示,button的text属性后面多了三个"1":
单步调试进入setText方法内部,发现该方法最终执行的实现是ManagedObject.setProperty:
我们可以通过上图右边调用栈里实现,复习本系列之前文章学到的两个知识点:
(1) 文章 深入学习SAP UI5框架代码系列之一:UI5 Module的懒加载机制 里提到的SAP UI5控件的原型继承链:
Button->Control->Element->ManagedObject->EventProvider->BaseObject
(2) 文章 HTML原生事件 VS SAP UI5 Semantic事件 里提到的从HTML原生的click事件到SAP UI5 press语义事件的映射逻辑。
setProperty的实现逻辑概述
注释写得很棒,不过setProperty的代码本身的执行逻辑也很清晰。
第1295行this.mProperties即SAP UI5控件的实例数据存储结构。上图右上角Watch面板里的含义是,当前setProperty方法调用,需要修改text属性,修改成新的值为"Button1".
1295行首先从this.mProperties中取出text属性修改前的值,存储在变量oOldValue里。
1298行调用this.validateProperty方法,检查传入setProperty的输入参数,即待修改的属性值oValue是否有效。
1300行判断修改前的值oOldValue,和待修改的输入值oValue是否相同。如果相同,当前setProperty调用没必要继续执行,直接返回。
在validateProperty内部,SAP UI5框架根据本系列前一篇文章 深入学习SAP UI5框架代码系列之四:SAP UI5控件的元数据实现 描述的逻辑, 取出控件text属性的元数据,得知该属性类型为string,访问权限为public:
每个不同类型的SAP UI5控件属性,根据其元数据的type字段,可以得到一个对应的类型描述器,如上图1409行oType所示。
描述器里包含一系列方法,其中normalize函数负责在写入新的属性值时,对输入值进行规则化处理。
在setProperty调用时,最后一个可选的输入参数bSuppressInvalidate,默认值为undefined,因此会执行1316行的invalidate方法,触发UI的重绘操作(rerendering)。
上图第1313行只是将新的属性值写入SAP UI5控件的实例数据存储结构里,该行代码执行完毕后,UI上的button标签文本并不会变化。
只有当UI界面重绘完成后,用户才能在浏览器里看到button标签的最新值。
上图1316行的invalidate方法,会以异步的方式触发UI重绘操作。异步操作的调度,采用JavaScript原生函数setTimeout, 该函数将renderPendingUIUpdates这个任务添加到JavaScript引擎任务队列的尾部,这样主线程一旦空闲(因为setTimeout第二个参数,即超时时间指定为0),就会执行renderPendingUIUpdates,以重绘UI区域里需要重绘,即属性值发生了变化的那些控件。
Button控件的重绘,最终通过其对应的渲染器,ButtonRenderer来实现,具体的渲染方法render的调用,如上图右部标注了数字4的调用栈栈帧所示。
关于SAP UI5控件的渲染器,请查看Jerry之前的文章 深入学习SAP UI5框架代码系列之二:UI5 控件的渲染器。
再回到ManagedObject.setProperty的方法主体。
1320行的this.updateModelProperty, 涉及到SAP UI5控件对应的模型更新,在代码1319行注释里提到,如果控件使用双向绑定方式同一个模型绑定,那么当UI控件属性发生变化时,对应的模型字段也应该被更新。这个模型字段的更新就实现在1320行的updateModelProperty函数里,Jerry的下一篇文章 UI5控件数据绑定的实现原理 会介绍。
ManagedObject.setProperty的末尾,会调用实现在原型链节点EventProvider上的fireEvent方法,抛出一个_change事件,包含发生该事件的控件id,发生change的属性名称,变化前和变化后的属性值。
虽然事件名称_change前面的下划线表明该事件用于SAP UI5框架内部处理,然而这只是一个弱约束,我们依旧可以在自己的应用程序里,使用下图高亮区域里button控件的attachEvent方法,来监听这个事件。
下图右部分调试器Watch面板里展示的是_change事件的负载,表明一个id为__button0的控件,text属性值从Jerry变成了Jerry1.
以上就是button控件的setText->setProperty的执行逻辑的大致介绍,了解了SAP UI5控件数据修改的原理,理解getText就容易多了。
前面介绍setProperty的时候提到了执行UI重绘的异步操作,发生在renderPendingUIUpdates函数里,这里button控件的渲染器ButtonRenderer的render方法会被调用。渲染器又调用button的getText方法,取出待渲染的button标签值。
而getText同setText类似,转而调用ManagedObject的getProperty方法:
getProperty的核心逻辑比setProperty简单得多,直接从控件实例数据存储结构mProperties里取出对应需要读取的属性值。
顺便说一句,Angular同SAP UI5一样,也有类似的UI异步重绘操作。
每当Angular内部维护的微任务队列为空时,(onMicrotaskEmpty), 触发tick操作:
tick操作调用detectChanges函数(相当于SAP UI5的renderPendingUIUpdates), detectChanges会递归调用refreshView, 刷新发生了属性变化的Angular控件。
本系列的下一篇文章,会介绍SAP UI5控件数据绑定的实现原理,感谢阅读。
系列目录
(0) SAP UI5应用开发人员了解UI5框架代码的意义
(1) SAP UI5 module懒加载机制
(2) SAP UI5 控件渲染机制
(3) HTML原生事件 VS SAP UI5 Semantic事件
(4) SAP UI5控件元数据的元数据实现
(5) SAP UI5控件的实例数据修改和读取逻辑(本文)
(6) SAP UI5控件数据绑定的实现原理
(7) SAP UI5控件数据绑定的三种模式:One Way,Two Way和OneTime实现原理比较
(8) SAP UI5控件ID的生成逻辑
(9) SAP UI5控件的多语言(国际化,Internationalization,i18n)支持的实现原理
(10) XML视图里的button控件
(11) button控件和它背后的DOM元素