这是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控件的事件处理,全文会围绕下图体现出的差别来论述。
首先用一个简略的例子来回顾HTML原生事件处理原理。
有这样一个简略的HTML页面,外面应用了一个HTML原生button标签,通过onclick="copyText"注册了一个名为copyText的事件处理函数。
点击按钮之后,响应函数copyText将Field1的值拷贝到Field2去。这个通过onclick注册的事件处理函数,在Chrome开发者工具里能够间接查看。
除了onclick之外,调用浏览器原生的addEventListener办法也能给DOM元素注册事件。
在企业级web利用里,DOM树的构造通常都不简略。例如仅仅蕴含一个简略button控件的SAP UI5利用,页面渲染进去后也会主动生成5个div标签:
如果每个须要响应事件的控件,都应用onclick或者addEventListener给DOM元素注册一个事件处理函数,随着DOM事件处理函数数量的减少,web利用的性能会升高。因而SAP UI5引入了另一种所谓Semantic(语义)事件的概念,来实现UI5控件的事件注册和响应工作。
应用Jerry文章 一个用于SAP UI5学习的脚手架利用,没有任何后盾API的依赖 提到的脚手架,开发一个只蕴含sap.ui.commons.button的UI5利用:
上图的Elements标签页里,显示的是SAP UI5利用渲染结束后,生成的HTML原生代码。里蕴含的button标签的生成逻辑,咱们曾经在前一篇文章 深刻学习SAP UI5框架代码系列之二:UI5 控件的渲染器 里介绍过了。
咱们采纳与前一个原生HTML button例子同样的操作形式,在Chrome开发者工具里查看UI5利用里该button的Event Listeners,却什么也没发现。
选中"Ancestors"后面的勾之后,一下子显示了很多条目进去:
开展条目中的click,发现SAP UI5把click事件注册在button标签的父节点,即id为content的div标签上了,如下图所示。
再看看UI5利用里sap.ui.commons.Button的事件注册代码:
这里并没有呈现HTML原生事件click的身影,而将一个蕴含了属性名称press,值为JavaScript函数的JavaScript对象,作为输出参数,传入了UI5 Button的构造函数里:
用户点击这个按钮时,触发的应该是名称为click的事件,和咱们在这里为press事件注册的处理函数有什么关系?
在UI5 button的实现源代码里能找到答案。切换到Chrome开发者工具的Sources标签页,快捷键Ctrl + O,输出button,抉择第一个后果Button-dbg.js:
这里能看到,press作为button反对的事件,定义在Button-dbg.js里:
上面这段代码的含意是,当UI5 button有click事件产生时,如果其自身处于enabled并且是visible状态,则fire一个Press事件(this.firePress()):
因而,正是Button实现里的这个onclick函数,实现了从事件click映射到事件press的工作。
上图调试器里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调用的事件响应函数。
下图展现的逻辑是:
(1) SAP UI5框架从第237行的控件事件注册表里,依据事件名称press,取出寄存其事件处理函数的数组;
(2) 遍历该数组,在for循环里用JavaScript function原型提供的call办法,对这些响应函数进行调用,实现事件响应:
至此又引出了一个新的问题:button控件的事件注册表mEventRegistry里的那惟一的条目,是何时填充进去的?
再回顾本系列第一篇文章里介绍的SAP UI5控件的原型链:
Button->Control->Element->ManagedObject->EventProvider->BaseObject.
UI5利用里这一行语句:
new sap.ui.commons.Button()
会顺次执行控件原型链上每一个节点对应的构造函数。控件事件注册表mEventRegistry的填充操作,就产生在EventProvider这个节点的构造函数里:
上图的变量oValue,就是我new一个button实例时传入的press事件的处理函数。在第1192行代码里,调用attachPress将oValue指向的函数进行注册。函数attachPress最终调用EventProvider的attachEvent办法,将键值对写入mEventRegistry:
至此有最初一个问题还未解答:本文结尾局部展现的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的值来确认。
以上图调用栈中绿色的线为分隔,绿线下方的代码,解决的是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树复杂度的减少而造成的利用性能降落。
引入Semantic事件后,UI5控件不间接响应HTML原生事件,而是通过一个叫做UIArea的实体,来接管用户触发的HTML原生事件,并将其dispatch给UI5控件,后者再将其映射成一一对应的Semantic事件,并调用应用程序里实现的响应函数。这里的UIArea能够类比成设计模式里的Facade(外观)模式,对SAP UI5的利用开发人员屏蔽了底层事件映射的复杂度。
上图的UIArea的详细描述,在SAP UI5官网文档里有记录。
下图高亮的一段对UIArea的论述,开展来讲就是Jerry本文的内容,大家感兴趣的能够移步这个链接持续浏览。
如果把本文提到的Semantic事件换个叫法,比方称其为虚构事件,那么很容易联想到Angular,Vue和React里引入的Virtual DOM(虚构DOM)概念。从实质上说,这些前端框架都采取减少框架实现复杂度的代价,引入一个两头形象层,来缩小间接在JavaScript层操作DOM层造成的性能开销。
顺便说一句,AngularJS里的控件注册实现,同SAP UI5思路统一:同样未采取将事件处理函数间接注册到HTML DOM元素上的机制。
下图是一个Angularjs利用,第22行的ng-click指令,通知Angularjs框架,超链接被点击后,依据模型字段name,进行排序。
Angularjs框架如何解析这个ng-click指令,并实现事件注册的?
在Angularjs利用bootstrap阶段,框架会遍历HTML DOM tree,递归调用compileNodes办法,逐个解析每一个蕴含了ng指令的元素:
当解析到蕴含了ng-click = "sortField = 'name'"的a标签时,调用Angular元素element的on办法,进行事件注册:
查看on办法的实现代码可知:Angularjs也并未将事件响应函数注册到DOM元素上,而是同SAP UI5一样,在框架内保护了一个控件事件注册表,this.$$listeners(SAP UI5的名称叫做mEventRegistry),采纳键值对的数据结构,来存储事件名称和其对应的事件响应函数。
Angularjs利用里,事件响应函数被调用时的调用栈截图:
对于SAP UI5和Angularjs的事件处理机制比拟的更多细节,能够参考我的SAP社区博客:
Compare Event handling mechanism: SAPUI5 and Angular
本系列下一篇文章介绍的内容:UI5控件元数据实现细节。
感激浏览。
系列目录
(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元素
更多Jerry的原创文章,尽在:"汪子熙":