深入学习SAP UI5框架代码系列之一:UI5 Module的懒加载机制
本文是深入學習SAP UI5框架代碼系列的第二篇文章。
系列目錄
-
SAP UI5應用開發人員了解UI5框架代碼的意義
-
UI5 module懶加載機制
-
UI5 控件渲染機制
-
HTML原生事件 VS SAP UI5 Semantic事件
-
UI5控件元數據實現細節
-
UI5控件的實例數據實現細節
-
UI5控件數據綁定的實現原理
-
UI5控件數據綁定的三種模式:One Way,Two Way和OneTime實現原理比較
-
UI5控件ID的生成邏輯
-
UI5控件的多語言(國際化,Internationalization,i18n)支持的實現原理
-
XML視圖里的button控件
-
button控件和它背后的DOM元素
通過Jerry前一篇文章 一個用于SAP UI5學習的腳手架應用,沒有任何后臺API的依賴 介紹的腳手架應用,創建一個只包含一個Button控件的UI5應用:
瀏覽器里打開,總共觸發了18個請求,網絡傳輸流量1.1MB, 頁面總共加載了5.1MB資源(見下圖底部紫色矩形框所示)。
順便說一說,為什么頁面加載的資源尺寸(5.1 MB)會大于網絡傳輸的數據量(1.1 MB)?
網上有一種說法,頁面加載的資源,是通過網絡加載的資源,以及從瀏覽器緩存讀取的資源總和,因此會出現Chrome開發者工具里顯示的頁面加載的資源尺寸大于網絡傳輸數據量的情況。
這種說法不完全正確。更準確的說,頁面加載資源統計的是前端頁面加載的所有資源,經過解壓之后的原始大小。
如圖,打開Chrome開發者工具的Use Large request rows選項, 就能顯示出經過網絡加載資源解壓縮過后的原始大小,如下圖所示:
以上說明來自Google官網:
https://developers.google.com/web/tools/chrome-devtools/network/reference#uncompressed
回到我們的UI5應用,Ctrl+Alt+Shift+P,選中"Use Debu Sources",讓SAP UI5加載調試版本的庫文件:
待button顯示在頁面之后,打開Chrome開發者工具Sources面板,能看到sap/ui文件夾下多出來一個commons文件夾:
回憶一下我們的腳手架應用的代碼里,新建了一個命名空間sap.ui.commons下的Button控件實例:
因此運行時,SAP UI5對應的Button Module會被加載。Button-dbg.js負責Button的生命周期管理和事件響應,ButtonRenderer-dbg.js負責將Button實例渲染成原生的HTML代碼。
切換到Network標簽頁,選擇任意一個Button Module加載的網絡請求,把鼠標hover到Initiator列上,在彈出窗口就能看到一個調用棧,從中就能觀察到是index.html即Button控件的消費者,觸發了這兩個Button Module的加載。
在index.html里實例化Button控件的代碼處設置斷點,重新刷新應用。因為sap.ui.commons.Button并不是原生的HTML element,所以調試器執行到代碼第11行并且單步執行后,會觸發Button Module的加載:
這就是SAP UI5 Module的懶加載機制:如果該頁面沒有用到Button控件,則對應的Button Module永遠不會被加載。
下圖sap-ui-core-dbg.js第26384行就是Button Module的加載入口,注意注釋里lazy stub for XXX的提示:
requireModule準備加載sap/ui/commons/button.js這個Module文件:
Module文件通過AJAX被加載后,SAP UI5得到的只是純字符串文本,還無法直接用其創建button實例。SAP UI5會調用瀏覽器原生API, window.eval(), 將button.js文件的字符串內容傳入該API,執行結果是一個JavaScript對象,也就是SAP UI5 Button Module的運行時實體。
SAP UI5運行時為所有的Module維護了一個注冊表,以鍵值對的數據結構存儲了這些Module的信息,鍵的數據類型為string,值類型即window.eval()執行加載好的JavaScript文件內容后返回的JavaScript對象。
Module的可能狀態為一系列枚舉值:INITIAL, LOADED, READY, FAILED, PRELOADED.
回到我的例子,因為我的代碼觸發了Button Module的第一次加載,所以代碼第16487行,將Module的狀態標注為INITIAL.
繼續調試:
- Line 16514: 將Button Module狀態設置為LOADING.
- Line 16517: 根據全局標志位window.sap-ui-loaddbg的值決定加載Button Module的普通版本還是調試版本。
- Line 16520: 根據Module名稱獲得待加載Module的url.
- Line 16525: 使用jQuery.AJAX加載Button-dbg.js.
因為該Module若不加載完成,則我們代碼里的new 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:
new sap.ui.commons.button這行語句看似僅僅是一個簡單的實例構造操作,背后卻隱藏著SAP UI5控件設計的思路。
SAP UI5的注釋寫的很清楚:首先用工廠方法創建一個新的空Button實例oInstance,然后再使用消費者調用new sap.ui.commons.Button時傳入的參數對oInstance進行enrich:
我們查看Button Module的源代碼,發現通過JavaScript的原型繼承,Button的prototype為Control:
查看SAP UI5官網上對sap.ui.core.Control的說明:
- Rendering: 每個SAP UI5控件都有對應的Renderer,被RenderManager調用負責生成原生的HTML代碼。
- 顯示/隱藏,Busy Indicator,支持關聯自定義的CSS樣式類,注冊瀏覽器事件。
Control的原型是Element:
Element是SAP UI5頁面的基本元素,主要用于UI5的內部實現。
Element的原型是ManagedObject:
因為這條原型鏈過長,Jerry就不一一截圖了,大家只需要記住結論:從Button控件出發,沿著它的原型鏈往上回溯,最后會到達BaseObject(相當于ABAP/Java里的Object).
Button->Control->Element->ManagedObject->EventProvider->BaseObject.
因此,在執行Button自己的構造函數之前,其原型鏈上每個節點的構造函數會依次執行一次:
本系列下一篇文章:UI5 控件渲染機制。
感謝閱讀。
更多Jerry的原創文章,盡在:“汪子熙”:
總結
以上是生活随笔為你收集整理的深入学习SAP UI5框架代码系列之一:UI5 Module的懒加载机制的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SAP Spartacus简介
- 下一篇: VB.net byval和byref