自從微信小程式出現以來,各種“小程式”如雨後春筍一般出現。事實證明小程式這種開發方式非常好,鴻蒙 JS UI 架構采用類似的方式也是在意料之中的。
一個小程式(在鴻蒙 OS 中,也就是 Ability)由多個頁面組成,每個頁面由三部分組成:
.hml 用來描述界面的元素
.css 用來描述界面的風格
.js 用來編寫處理事件邏輯
我們來看個例子:
index.hml
index.css
index.js
2. 工作原理
要了解它的工作原理,先研究一下編譯之後的代碼是非常重要的。上面的三個檔案,編譯之後會生成一個檔案,其位置在:./entry/build/intermediates/res/debug/lite/assets/js/default/pages/index/index.js
index.hml 變成了建立函數:
index.css 變成了 JSON 檔案。
這種處理方式很妙,把 JS 不擅長處理的 XML/CSS 轉換成了 JS 代碼和 JSON 對象,這個轉換由工具完成,避免了運作時的開銷。
在沒有研究編譯之後的代碼時,我嘗試在 ace/graphic 兩個包中尋找解析 HML 的代碼,讓我驚訝的是沒有找到相關代碼。看了這些生成的代碼之後才恍然大悟。
在 C++的代碼中,建立控件的過程如下:
調用 JS 的 _c 函數時,進入 C++的函數 RenderModule::CreateElement
RenderModule::CreateElement 的實作如下:
把 控件的 tag 轉換成控件的 ID
調用 ComponentFactory::CreateComponent 建立控件
調用控件的 Render 建立真正的控件和子控件。
那麼第一個_c 函數是在哪裡調用的呢?答案很明顯,進入一個頁面的時候,會調用 RenderPage 渲染頁面:
這裡會調用 JS 的 render 函數:
這裡的 renderFunction 又是哪個函數呢?這需要看看 ViewModel 的構造函數:
這裡的$render 是由 options 提供的,而 options 又是從哪裡來的呢?再來看看編譯後的代碼:
把 options.render 列印出來,可以看到
正是前面那個建立控件的函數。
從前面的代碼,很容易了解初始渲染時資料的綁定過程。比如 text 的 value 屬性從一個匿名函數擷取,它包裝了 xml 中些的表達式:
而運作時,資料變化時,需要監控匿名函數的值變化,變化時會調用下面這個函數,它負責更新控件的屬性。
在 initState 函數中,關注了 data 的變化:
條件渲染由_i 函數處理,它有兩個參數:
第一個參數是一個函數,決定是否進行渲染。
第二個參數是一個函數,決定如何進行渲染。
如:
編譯成:
C++中對應這個函數:
資料變化時,重新進行根據條件進行渲染,執行下面的函數:
關注資料變化的回調函數。
這個做法我覺得很有創意:
不需要搞虛拟 DOM 進行比較。
不需要重建立立整個 DOM。