這是Jerry 2021年的第 6篇文章,也是汪子熙公眾號總共第 277 篇原創文章。系列目錄(0) SAP UI5應用開發人員了解UI5框架代碼的意義(1) SAP UI5 module懶加載機制(2) SAP UI5 控制項渲染機制(3) HTML原生事件 VS SAP UI5 Semantic事件(4) SAP UI5控制項元數據的元數據實現(5) SAP UI5控制項的實例數據修改和讀取邏輯(本文)(6) SAP UI5控制項數據綁定的實現原理(7) SAP UI5控制項數據綁定的三種模式:One Way,Two Way和OneTime實現原理比較(8) SAP UI5控制項ID的生成邏輯(9) SAP UI5控制項的多語言(國際化,Internationalization,i18n)支持的實現原理(10) XML視圖裡的button控制項(11) button控制項和它背後的DOM元素本文我們將通過研究button控制項的setText和getText方法實現,來學習SAP UI5控制項的實例數據修改和讀取邏輯。下圖是一段簡單的SAP UI5代碼:每點擊一次button,就會在press事件的響應函數裡,給button的text屬性值尾部附上一個字符「1」.
點了三次按鈕後,其渲染出的HTML代碼如下圖所示,button的text屬性後面多了三個"1":
單步調試進入setText方法內部,發現該方法最終執行的實現是ManagedObject.setProperty:
我們可以通過上圖右邊調用棧裡實現,複習本系列之前文章學到的兩個知識點:(1) 文章 深入學習SAP UI5框架代碼系列之一:UI5 Module的懶加載機制裡提到的SAP UI5控制項的原型繼承鏈:Button->Control->Element->ManagedObject->EventProvider->BaseObject(2) 文章 HTML原生事件 VS SAP UI5 Semantic事件 裡提到的從HTML原生的click事件到SAP UI5 press語義事件的映射邏輯。setProperty的實現邏輯概述注釋寫得很棒,不過setProperty的代碼本身的執行邏輯也很清晰。
第1295行this.mProperties即SAP UI5控制項的實例數據存儲結構。上圖右上角Watch面板裡的含義是,當前setProperty方法調用,需要修改text屬性,修改成新的值為"Button1".1295行首先從this.mProperties中取出text屬性修改前的值,存儲在變量oOldValue裡。1298行調用this.validateProperty方法,檢查傳入setProperty的輸入參數,即待修改的屬性值oValue是否有效。1300行判斷修改前的值oOldValue,和待修改的輸入值oValue是否相同。如果相同,當前setProperty調用沒必要繼續執行,直接返回。在validateProperty內部,SAP UI5框架根據本系列前一篇文章 深入學習SAP UI5框架代碼系列之四:SAP UI5控制項的元數據實現描述的邏輯, 取出控制項text屬性的元數據,得知該屬性類型為string,訪問權限為public:
每個不同類型的SAP UI5控制項屬性,根據其元數據的type欄位,可以得到一個對應的類型描述器,如上圖1409行oType所示。描述器裡包含一系列方法,其中normalize函數負責在寫入新的屬性值時,對輸入值進行規則化處理。
在setProperty調用時,最後一個可選的輸入參數bSuppressInvalidate,默認值為undefined,因此會執行1316行的invalidate方法,觸發UI的重繪操作(rerendering)。
上圖第1313行只是將新的屬性值寫入SAP UI5控制項的實例數據存儲結構裡,該行代碼執行完畢後,UI上的button標籤文本並不會變化。只有當UI界面重繪完成後,用戶才能在瀏覽器裡看到button標籤的最新值。上圖1316行的invalidate方法,會以異步的方式觸發UI重繪操作。異步操作的調度,採用JavaScript原生函數setTimeout, 該函數將renderPendingUIUpdates這個任務添加到JavaScript引擎任務隊列的尾部,這樣主線程一旦空閒(因為setTimeout第二個參數,即超時時間指定為0),就會執行renderPendingUIUpdates,以重繪UI區域裡需要重繪,即屬性值發生了變化的那些控制項。
Button控制項的重繪,最終通過其對應的渲染器,ButtonRenderer來實現,具體的渲染方法render的調用,如上圖右部標註了數字4的調用棧棧幀所示。關於SAP UI5控制項的渲染器,請查看Jerry之前的文章 深入學習SAP UI5框架代碼系列之二:UI5 控制項的渲染器。再回到ManagedObject.setProperty的方法主體。1320行的this.updateModelProperty, 涉及到SAP UI5控制項對應的模型更新,在代碼1319行注釋裡提到,如果控制項使用雙向綁定方式同一個模型綁定,那麼當UI控制項屬性發生變化時,對應的模型欄位也應該被更新。這個模型欄位的更新就實現在1320行的updateModelProperty函數裡,Jerry的下一篇文章 UI5控制項數據綁定的實現原理會介紹。
ManagedObject.setProperty的末尾,會調用實現在原型鏈節點EventProvider上的fireEvent方法,拋出一個_change事件,包含發生該事件的控制項id,發生change的屬性名稱,變化前和變化後的屬性值。
雖然事件名稱_change前面的下劃線表明該事件用於SAP UI5框架內部處理,然而這只是一個弱約束,我們依舊可以在自己的應用程式裡,使用下圖高亮區域裡button控制項的attachEvent方法,來監聽這個事件。下圖右部分調試器Watch面板裡展示的是_change事件的負載,表明一個id為__button0的控制項,text屬性值從Jerry變成了Jerry1.
以上就是button控制項的setText->setProperty的執行邏輯的大致介紹,了解了SAP UI5控制項數據修改的原理,理解getText就容易多了。前面介紹setProperty的時候提到了執行UI重繪的異步操作,發生在renderPendingUIUpdates函數裡,這裡button控制項的渲染器ButtonRenderer的render方法會被調用。渲染器又調用button的getText方法,取出待渲染的button標籤值。
而getText同setText類似,轉而調用ManagedObject的getProperty方法:
getProperty的核心邏輯比setProperty簡單得多,直接從控制項實例數據存儲結構mProperties裡取出對應需要讀取的屬性值。
順便說一句,Angular同SAP UI5一樣,也有類似的UI異步重繪操作。每當Angular內部維護的微任務隊列為空時,(onMicrotaskEmpty), 觸發tick操作:
tick操作調用detectChanges函數(相當於SAP UI5的renderPendingUIUpdates), detectChanges會遞歸調用refreshView, 刷新發生了屬性變化的Angular控制項。
本系列的下一篇文章,會介紹SAP UI5控制項數據綁定的實現原理,感謝閱讀。
更多閱讀(0) SAP UI5應用開發人員了解UI5框架代碼的意義(1) SAP UI5 module懶加載機制(2) SAP UI5 控制項渲染機制(3) HTML原生事件 VS SAP UI5 Semantic事件(4) SAP UI5控制項元數據的元數據實現(5) SAP UI5控制項的實例數據修改和讀取邏輯(本文)(6) SAP UI5控制項數據綁定的實現原理(7) SAP UI5控制項數據綁定的三種模式:One Way,Two Way和OneTime實現原理比較(8) SAP UI5控制項ID的生成邏輯(9) SAP UI5控制項的多語言(國際化,Internationalization,i18n)支持的實現原理(10) XML視圖裡的button控制項(11) button控制項和它背後的DOM元素