深入学习SAP UI5框架代码系列之三:HTML原生事件 VS UI5 Semantic事件

简介: (0) SAP UI5应用开发人员了解UI5框架代码的意义(1) UI5 module懒加载机制(2) UI5 控件渲染机制(3) HTML原生事件 VS SAP UI5 Semantic事件(本文)(4) UI5控件元数据实现细节(5) UI5控件的实例数据实现细节(6) UI5控件数据绑定的实现原理(7) UI5控件数据绑定的三种模式:One Way,Two Way和OneTime实现原理比较(8) UI5控件ID的生成逻辑(9) UI5控件的多语言(国际化,Internationalization,i18n)支持的实现原理(10) XML视图里的b

这是Jerry 2020年的第80篇文章,也是汪子熙公众号总共第262篇原创文章。


系列目录


(0) SAP UI5应用开发人员了解UI5框架代码的意义


(1) UI5 module懒加载机制


(2) UI5 控件渲染机制


(3) HTML原生事件 VS SAP UI5 Semantic事件(本文)


(4) UI5控件元数据实现细节


(5) UI5控件的实例数据实现细节


(6) UI5控件数据绑定的实现原理


(7) UI5控件数据绑定的三种模式:One Way,Two Way和OneTime实现原理比较


(8) UI5控件ID的生成逻辑


(9) UI5控件的多语言(国际化,Internationalization,i18n)支持的实现原理


(10) XML视图里的button控件


(11) button控件和它背后的DOM元素


本文将讨论SAP UI5控件的事件处理,全文会围绕下图表现出的差异来阐述。


image.png


首先用一个简单的例子来回顾HTML原生事件处理原理。


有这样一个简单的HTML页面,里面使用了一个HTML原生button标签,通过οnclick="copyText"注册了一个名为copyText的事件处理函数。


image.png


点击按钮之后,响应函数copyText将Field1的值拷贝到Field2去。这个通过onclick注册的事件处理函数,在Chrome开发者工具里可以直接查看。


image.png


除了onclick之外,调用浏览器原生的addEventListener方法也能给DOM元素注册事件。


image.png


在企业级web应用里,DOM树的结构通常都不简单。例如仅仅包含一个简单button控件的SAP UI5应用,页面渲染出来后也会自动生成5个div标签:


image.png


如果每个需要响应事件的控件,都使用onclick或者addEventListener给DOM元素注册一个事件处理函数,随着DOM事件处理函数数量的增加,web应用的性能会降低。因此SAP UI5引入了另一种所谓Semantic(语义)事件的概念,来完成UI5控件的事件注册和响应工作。


使用Jerry文章 一个用于SAP UI5学习的脚手架应用,没有任何后台API的依赖 提到的脚手架,开发一个只包含sap.ui.commons.button的UI5应用:


image.png


上图的Elements标签页里,显示的是SAP UI5应用渲染完毕后,生成的HTML原生代码。里包含的button标签的生成逻辑,我们已经在前一篇文章 深入学习SAP UI5框架代码系列之二:UI5 控件的渲染器 里介绍过了。


我们采用与前一个原生HTML button例子同样的操作方式,在Chrome开发者工具里检查UI5应用里该button的Event Listeners,却什么也没发现。


选中"Ancestors"前面的勾之后,一下子显示了很多条目出来:


image.png


展开条目中的click,发现SAP UI5把click事件注册在button标签的父节点,即id为content的div标签上了,如下图所示。


image.png


再看看UI5应用里sap.ui.commons.Button的事件注册代码:


image.png


这里并没有出现HTML原生事件click的身影,而将一个包含了属性名称press,值为JavaScript函数的JavaScript对象,作为输入参数,传入了UI5 Button的构造函数里:


image.png


用户点击这个按钮时,触发的应该是名称为click的事件,和我们在这里为press事件注册的处理函数有什么关系?


在UI5 button的实现源代码里能找到答案。切换到Chrome开发者工具的Sources标签页,快捷键Ctrl + O,输入button,选择第一个结果Button-dbg.js:


image.png


这里能看到,press作为button支持的事件,定义在Button-dbg.js里:


image.png


下面这段代码的含义是,当UI5 button有click事件发生时,如果其本身处于enabled并且是visible状态,则fire一个Press事件(this.firePress()):


image.png


因此,正是Button实现里的这个onclick函数,实现了从事件click映射到事件press的任务。


image.png


上图调试器里168行的this.firePress调用,最终如何成功地调用到UI5程序里针对press事件注册的处理函数的呢?


还记得这个系列的前一篇文章 深入学习SAP UI5框架代码系列之一:UI5 Module的懒加载机制 里介绍的一个知识点吗?


SAP UI5运行时为所有的Module维护了一个注册表,以键值对的数据结构存储了这些Module的信息,键的数据类型为string,值类型即window.eval()将加载好的JavaScript文件内容作为输入参数,执行后返回的JavaScript对象。


类似的原理,SAP UI5里每个控件都维护了一个键值对结构的事件注册表mEventRegistry, 键的数据类型string,存储事件名称,值类型为数组,里面存放了针对该事件,应用程序实现的响应函数。


下图展示的是我脚手架应用里的button控件的事件注册表,只包含一条记录,键为press,值为一个数组,里面唯一的元素即我在脚手架应用里实现的包含了alert调用的事件响应函数。


image.png


下图展示的逻辑是:


(1) SAP UI5框架从第237行的控件事件注册表里,根据事件名称press,取出存放其事件处理函数的数组;


(2) 遍历该数组,在for循环里用JavaScript function原型提供的call方法,对这些响应函数进行调用,完成事件响应:


image.png


至此又引出了一个新的问题:button控件的事件注册表mEventRegistry里的那唯一的条目,是何时填充进去的?


再回忆本系列第一篇文章里介绍的SAP UI5控件的原型链:


Button->Control->Element->ManagedObject->EventProvider->BaseObject.


UI5应用里这一行语句:


new sap.ui.commons.Button()


会依次执行控件原型链上每一个节点对应的构造函数。控件事件注册表mEventRegistry的填充操作,就发生在EventProvider这个节点的构造函数里:


image.png


上图的变量oValue,就是我new一个button实例时传入的press事件的处理函数。在第1192行代码里,调用attachPress将oValue指向的函数进行注册。函数attachPress最终调用EventProvider的attachEvent方法,将键值对写入mEventRegistry:


image.png


至此有最后一个问题还未解答:本文开头部分展示的Chrome开发者工具里,SAP UI5页面渲染后生成的button标签,在Event Listeners一栏里观察不到任何响应函数。而在其父节点,id为content的div标签里,在click事件下却能观察到响应函数。


Button父节点的div标签上的click方法,和本文讨论了这么长时间的button事件注册表里的press事件,到底有何关系?


按钮被点击时,查看调试器里显示的调用栈最外一层,发现SAP UI5的jquery-dbg.js, 响应的是HTML原生的click事件,且触发该事件的对象的的确确是id为content的div标签,而不是button标签,这一点可以从event.currentTarget的值来确认。


image.png


以上图调用栈中绿色的线为分隔,绿线下方的代码,处理的是HTML原生的点击事件click,同时完成了将click事件,经div投递给其子节点,button标签的任务。


绿线上方的Button.onclick, 前文我们已经阐述过,通过this.firePress将click事件映射成press事件,后续SAP UI5的所有事件处理,均围绕这个press事件进行。


按照SAP UI5开发团队大佬Andreas Kunz的介绍,button这种press事件称为Semantic事件。同HTML原生的click事件直接通过onclick或addEventListener注册在HTML DOM元素上不同,Semantic event的注册和调用都是通过SAP UI5框架的JavaScript代码施加在SAP UI5自行实现的控件上,比HTML原生的DOM事件处理和响应轻量得多,能避免随着DOM树复杂度的增加而造成的应用性能下降。


image.png


引入Semantic事件后,UI5控件不直接响应HTML原生事件,而是通过一个叫做UIArea的实体,来接收用户触发的HTML原生事件,并将其dispatch给UI5控件,后者再将其映射成一一对应的Semantic事件,并调用应用程序里实现的响应函数。这里的UIArea可以类比成设计模式里的Facade(外观)模式,对SAP UI5的应用开发人员屏蔽了底层事件映射的复杂度。


上图的UIArea的详细描述,在SAP UI5官方文档里有记载。


下图高亮的一段对UIArea的阐述,展开来讲就是Jerry本文的内容,大家感兴趣的可以移步这个链接继续阅读。


image.png


如果把本文提到的Semantic事件换个叫法,比如称其为虚拟事件,那么很容易联想到Angular,Vue和React里引入的Virtual DOM(虚拟DOM)概念。从本质上说,这些前端框架都采取增加框架实现复杂度的代价,引入一个中间抽象层,来减少直接在JavaScript层操作DOM层造成的性能开销。


顺便说一句,AngularJS里的控件注册实现,同SAP UI5思路一致:同样未采取将事件处理函数直接注册到HTML DOM元素上的机制。


下图是一个Angularjs应用,第22行的ng-click指令,告诉Angularjs框架,超链接被点击后,根据模型字段name,进行排序。


image.png


Angularjs框架如何解析这个ng-click指令,并完成事件注册的?


在Angularjs应用bootstrap阶段,框架会遍历HTML DOM tree,递归调用compileNodes方法,逐一解析每一个包含了ng指令的元素:


image.png


当解析到包含了ng-click = "sortField = ‘name’"的a标签时,调用Angular元素element的on方法,进行事件注册:


image.png


查看on方法的实现代码可知:Angularjs也并未将事件响应函数注册到DOM元素上,而是同SAP UI5一样,在框架内维护了一个控件事件注册表,this.$$listeners(SAP UI5的名称叫做mEventRegistry),采用键值对的数据结构,来存储事件名称和其对应的事件响应函数。


image.png


Angularjs应用里,事件响应函数被调用时的调用栈截图:

image.png


相关文章
|
2天前
|
自然语言处理 开发者 Python
Markdown 是一种轻量级标记语言,它允许人们使用易读易写的纯文本格式编写文档,然后转换成格式丰富的 HTML 内容。Markdown 的语法简洁明了、学习容易,而且功能比纯文本更强。
Markdown 是一种轻量级标记语言,它允许人们使用易读易写的纯文本格式编写文档,然后转换成格式丰富的 HTML 内容。Markdown 的语法简洁明了、学习容易,而且功能比纯文本更强。
10 2
|
14天前
|
JavaScript 前端开发 Java
前端网页开发学习(HTML+CSS+JS)有这一篇就够!(二)
前端网页开发学习(HTML+CSS+JS)有这一篇就够!(二)
|
14天前
|
前端开发 数据安全/隐私保护
前端网页开发学习(HTML+CSS+JS)有这一篇就够!(一)
前端网页开发学习(HTML+CSS+JS)有这一篇就够!(一)
|
20天前
|
数据采集 移动开发 前端开发
HTML5 + CSS3 编程规范:构筑清晰、可维护的代码基石
HTML5 + CSS3 编程规范:构筑清晰、可维护的代码基石
|
20天前
HTML中如何插入空格,HTML空格代码,多种HTML空格写法
HTML中如何插入空格,HTML空格代码,多种HTML空格写法
12 0
|
24天前
爱心代码---html代码合集他来咯(2)
爱心代码---html代码合集他来咯
24 0
|
24天前
爱心代码---html代码合集他来咯(1)
爱心代码---html代码合集他来咯
29 0
|
25天前
|
数据安全/隐私保护
杨老师课堂之网页制作HTML的学习入门-含有案例2
杨老师课堂之网页制作HTML的学习入门-含有案例
7 0
|
2月前
|
前端开发 搜索推荐 开发者
SAP UI5 sap.m.Column 控件的 minScreenWidth 属性介绍
SAP UI5 sap.m.Column 控件的 minScreenWidth 属性介绍
|
2月前
|
JavaScript 前端开发 开发者
SAP UI5 控件 sap.m.ListBase 的 inset 属性的作用介绍
SAP UI5 控件 sap.m.ListBase 的 inset 属性的作用介绍