通過 Flutter 學習一下 UI 技術架構

不小心被推到前端話題了我,下面只是一些臨時的記錄而已,不推薦閱讀,打擾了

還很亂套,做個記錄先。。。

Flutter 的三層結構

  1. widget 層:用來定義state、props 和組件之間的關係,以及 widget 上下級關係
  2. element 層:更像是 widget 層的 shadow layer,element 實際上負責構成了 flutter 內部的 UI 樹
  3. render object 層:用於封裝 UI 底層的渲染邏輯

常見的 widget 和 element 基本上是一對一的關係,如下圖所示:

圖中 w1/w2 表示 widget,其中 w1 是 w2 的父節點。E1 和 E2 表示 Element。上下級的 Widget 之間具有 build 關係,w2 是通過調用 w1 的 build 方法創建的。

實際在界面渲染過程中,flutter 並沒有直接使用 Widget 這個數據結構來表示組件樹,而是額外用 Element 類來表示。如上圖,每個 Element 實例是通過調用 Widget 的 createElement 方法創建的,創建的 Element 保留了 Widget 實例的引用。

在渲染整個 UI 時,widget 和 element 之間的調用關係是:

  1. 系統先創建最上層的 w1
  2. 調用 w1.createElement 方法創建同級關係的 e1
  3. e1 再調用 w1 的 build 方法,創建 w1 的子節點 w2
  4. 然後在 w2 上重複步驟 1 ~ 3,逐級的將一層層的 widget 和 element 創建出來,形成整個 UI 結構

可以看到的是 Widget 上下級之間是沒有直接的引用關係的,而 Element 上下級之間是有直接引用關係的,最後的 UI 樹效果如下圖所示:

其中,圖中藍色的節點表示 Element 實例,綠色節點表示 Widget 實例。


上面介紹的 Widget 和 Element 其實是主要是封裝一些 「容器」 類組件,像 Text 等「葉子」節點類型的組件,則沒有 _child 而是有 renderObject 屬性。就以 Text 為例。創建 Text 節點的時候其實會創建以下幾個對象:

  1. Widget: Text 實例
  2. Widget: RichText 實例,作為 Text 示例的子節點
  3. Element: LeafRenderObjectElement 實例
  4. Render Object: RenderParagraph

渲染流程也和上面介紹的流程不一樣:

  1. Text 創建 ComponentElement 實例
  2. ComponentElement 實例反過來調用 Text build 方法,創建 RichText 實例,然後 ComponentElement 實例 updateChild 方法去更新 RichText 實例,這個過程會調用到 RichText 實例的 update 方法
  3. RichText 實例的 update 方法當中會調用 RichText 實例的 updateRenderObject 方法
  4. 調用 updateRenderObject 方法,而不是更新 child 節點,因為是葉子節點嘛,本來就沒有子節點了
  5. updateRenderObject 方法當中會更新 RenderParagraph 的屬性,比如 textAlign、text 等
  6. RenderParagraph 實例的屬性更新時,會將自身登記到渲染模塊的 dirty nodes 當中去
  7. 等 UI 樹更新完以後,渲染模塊會遍歷 dirty nodes,進行最後的 UI 渲染環節

結合上面的內容,UI 樹的結構直觀上應該是下圖這個樣子:

其中紅色的對象表示的是各種 RenderObject 子類實例


總結:

  1. 大部分的 Widget 和 Element 是一對一關係
  2. Element 實例組成了 UI 樹
  3. 葉子節點的 Element 實例會包含 RenderObject 實例
  4. RenderObject 的各種子類封裝了最終的繪製邏輯,底層用的是 canvas 介面來繪製
  5. 更新 UI 樹的過程中:
    1. widget 用來構建新的 Element 樹
    2. 葉子節點類型 Element 實例會包含 RenderObject 實例
    3. 葉子節點屬性更新時,實際更新的是 RenderObject 中的實例
  6. 當 Element UI 樹更新完成之後,渲染模塊會遍歷 dirty nodes 來重新布局和渲染

問題

  1. Text 節點是如何渲染的呢
    1. 首先調用 pipeline owner 的 flushLayout
    2. 然後調用它的 flushPaint 進行渲染
      1. pipeline owner 會將 dirty nodes 根據深度來進行排序,層次淺的先繪製
      2. 繪製過程當中會調用 RenderObject 的 paint 方法
      3. paint 方法當中封裝的繪製 canvas 的邏輯
      4. RenderParagraph 方法當中封裝的是將文本繪製到 canvas 上面的邏輯,主要是用了一個叫做 TextPainter 的模塊

推薦閱讀:

TAG:技術架構 | 前端開發 | Flutter |