本文是深入学习SAPUI5框架代码系列文章的第二篇。系列目录SAPUI5应用开发者理解UI5框架代码含义UI5模块懒加载机制UI5控件渲染机制HTML原生事件VSSAPUI5语义事件UI5控件元数据实现详解UI5控件实例数据实现详解UI5控件数据绑定实现原理UI5控件数据绑定的三种模式:OneWay,TwoWayandOneTime实现原理对比UI5控件ID生成逻辑UI5控件多语言(Internationalization,Internationalization,i18n)支持实现原理XML视图中的Button控件Button控件及其背后的DOM元素通过Jerry之前的文章,一个SAP的脚手架应用UI5学习,无任何后台API依赖。创建一个只包含一个Button控件的UI5应用:在浏览器中打开,一共触发了18次请求,网络传输流量1.1MB,页面一共加载了5.1MB资源(见下面紫色矩形框)下图底部)。顺便问一下,为什么页面加载的资源大小(5.1MB)大于通过网络传输的数据量(1.1MB)?网上有一种说法,页面加载的资源是通过网络加载的资源和从浏览器缓存中读取的资源之和。因此,在Chrome开发者工具中显示的页面加载的资源大小可能大于网络传输数据量。.这种说法并不完全正确。更准确的说,页面加载资源统计是前端页面加载的所有资源解压后的原始大小。如图,打开Chrome开发者工具的UseLargerequestrows选项,可以显示网络加载的资源解压后的原始大小,如下图:以上描述来自谷歌官网:https://developers.google.com...回到我们的UI5应用,Ctrl+Alt+Shift+P,选择“UseDebuSources”,让SAPUI5加载debug版本的库文件:按钮后显示在页面上,打开Chrome开发者工具Sources面板,可以看到sap/ui文件夹下多了一个commons文件夹:回想一下我们脚手架应用的代码中,在命名空间sap下新建了一个Button控件实例.ui.commons被创建:所以运行时,相应的SAPUI5ButtonModule会被加载。Button-dbg.js负责Button的生命周期管理和事件响应,ButtonRenderer-dbg.js负责将Button实例渲染成原生HTML代码。切换到Network选项卡,选择ButtonModule加载的任意一个网络请求,将鼠标悬停在Initiator栏,在弹窗中可以看到一个调用栈,从中可以看出index.html是消费者Button控件的,它会触发这两个Button模块的加载。在index.html中实例化Button控件的代码处打断点,刷新应用。因为sap.ui.commons.Button不是原生的HTML元素,调试器执行到代码的第11行,一步步执行后,会触发ButtonModule的加载:这是SAPUI5的懒加载机制Module:如果页面中没有使用Button控件,则永远不会加载对应的ButtonModule。下图中sap-ui-core-dbg.js的26384行是ButtonModule的加载入口,注意注释中lazystubforXXX的提示:requireModuleisreadytoloadtheModulefilesap/ui/commons/button.js:ModulefilepassedAJAX加载后,SAPUI5获取的只是纯字符串文本,不能直接用它来创建按钮实例。SAPUI5将调用浏览器的本机APIwindow.eval(),并将button.js文件的字符串内容传递给API。执行结果是一个JavaScript对象,是SAPUI5ButtonModule的运行时实体。SAPUI5运行时为所有Modules维护了一个注册表,将这些Modules的信息存储在键值对的数据结构中。key的数据类型是string,value类型是window.eval()来执行加载的JavaScript文件的内容。之后返回的JavaScript对象。Module的可能状态是一系列枚举值:INITIAL、LOADED、READY、FAILED、PRELOADED。回到我的例子,因为我的代码触发了ButtonModule的第一次加载,所以代码行16487,Module的状态被标记为INITIAL。继续调试:第16514行:将按钮模块状态设置为LOADING。16517行:根据全局标志window.sap-ui-loaddbg的值,判断加载普通版还是调试版ButtonModule。第16520行:根据Module名称获取要加载的Module的url。第16525行:使用jQuery.AJAX加载Button-dbg.js。因为如果不加载Module,我们代码中新建的sap.ui.commons.Button是无法继续往下走的,所以这里的AJAX调用是以同步方式(async=false)进行的。在其加载成功的回调函数中,Module状态设置为LOADED,response变量包含Button-dbg.js的文本内容。Module状态为LOADED,表示其文本内容已经加载完毕,可以交给16543行的execModule函数执行(注意这个函数上面的IF条件)。代码16612,调试器显示:变量sScript中包含Button-dbg.js的文本内容,window.eval()执行完成后,Module的状态设置为READY:newsap.ui.commons.button行语句看似只是一个简单的实例构造操作,但背后隐藏着SAPUI5控件设计的思想。SAPUI5的注释很明确:首先使用工厂方法新建一个空的Button实例oInstance,然后使用消费者调用newsap.ui.commons.Button时传入的参数来丰富oInstance:我们看ButtonModule源码源码,通过JavaScript原型继承发现Button的原型是Control:查看SAPUI5官网关于sap.ui.core.Control的说明:Rendering:每个SAPUI5控件都有对应的Renderer,它负责被RenderManager调用生成原生HTML代码。显示/隐藏、BusyIndicator、支持关联自定义CSS样式类、注册浏览器事件。Control的原型是Element:Element是SAPUI5页面的基本元素,主要用于UI5的内部实现。Element的原型是ManagedObject:因为这个原型链太长,Jerry就不一一截图了。你只需要记住结论:从Button控件开始,沿着它的原型链回溯,最后到达BaseObject(ABAP/Java中的Object)。Button->Control->Element->ManagedObject->EventProvider->BaseObject。因此,在执行Button自身的构造函数之前,其原型链上每个节点的构造函数都会依次执行一次:本系列下一篇文章:UI5控件渲染机制。谢谢阅读。更多Jerry原创文章在这里:《王子熙》:
