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

45次阅读

共计 4755 个字符,预计需要花费 12 分钟才能阅读完成。

这是 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 的原创文章,尽在:” 汪子熙 ”:

正文完
 0