天天看點

淺析weex之vdom渲染前言一. 檔案結構二. 主要類分析三. 初始化過程四. 資料更新過程總結

前段時間進行了weex頁面嘗試, 頁面滾動加載渲染得非常流暢, 讓h5頁面擁有了native般的體驗。

weex代碼結構如下,重點關注其js-framework實作。

閱讀js-framework代碼,我整理了一份思維導圖。

淺析weex之vdom渲染前言一. 檔案結構二. 主要類分析三. 初始化過程四. 資料更新過程總結

framework.js是instance建立的入口,可以從這個檔案開始自頂向下地閱讀代碼,了解其工作原理。可以重點了解它的dom結構,初始化過程,資料更新過程,下面我也将從這幾個方面進行描述。

淺析weex之vdom渲染前言一. 檔案結構二. 主要類分析三. 初始化過程四. 資料更新過程總結

weex的dom結構由<code>document</code>、<code>element</code>、<code>comment</code>三類組成。element建立普通節點,comment用于建立frag block節點。每個節點都有一個唯一的<code>ref</code>值,可以很友善地在文檔中被查詢到,同時記錄其父節點<code>parentref</code>,通過這種’雙向連結清單‘的操作可以友善進行節點拼接和擷取。文檔樹節點document記錄整個dom的結構,同時在document上綁定eventmanager事件處理器和listener監聽操作處理器。<code>eventmanager</code>記錄每個綁定了事件的節點和它對應的事件處理函數,提供事件的添加、删除和觸發。<code>listener</code>提供了dom操作轉化為callnative的能力,通過将每一個操作轉化為對應類型的actions,如<code>createbody</code>、<code>addelement</code>,并将每一個actions記錄updates數組。

淺析weex之vdom渲染前言一. 檔案結構二. 主要類分析三. 初始化過程四. 資料更新過程總結

weex複用了 vue.js 的資料監聽和依賴收集的代碼實作。通過observer、directive、watcher之間的協作,建立資料(model)和視圖(view)的關聯關系:

observer 對 data 進行了監聽,并且提供訂閱某個資料項的變化的能力

compiler解析template,并解析其中的 directive,得到每一個 directive 所依賴的資料項及其更新方法

watcher 把上述兩部分結合起來,即把 directive 中的資料依賴訂閱在對應資料的 observer 上,這樣當資料變化的時候,就會觸發 observer,進而觸發相關依賴對應的視圖更新方法。

當我們在浏覽器中輸入我們的bundle位址,其解析渲染為html過程大緻可以分解為createinstance-&gt;initinstace-&gt;run bundle-&gt;define-&gt;boostrap-&gt;create vm-&gt;生命周期函數。可細化為下面這些步驟:

淺析weex之vdom渲染前言一. 檔案結構二. 主要類分析三. 初始化過程四. 資料更新過程總結

<code>initinstance</code>: 根據webpack打包後的js代碼來定義執行個體。

<code>define</code>: 解析代碼中的<code>__weex_define__("@weex-component/bottom-bar")</code>定義的component,包含依賴的子元件。并将component記錄到<code>customcomponentmap[name] = exports</code>數組中,維護元件與元件代碼的對應關系。由于會依賴子元件,是以會被多次調用。

define執行後的appinstance執行個體結構:

淺析weex之vdom渲染前言一. 檔案結構二. 主要類分析三. 初始化過程四. 資料更新過程總結

<code>bootstrap</code>:解析代碼中的<code>__weex_bootstrap__("@weex-component/30d1c553f95b5f87afb6a1cff70a7bbd")</code>執行目前頁面,提取customcomponentmap中記錄的元件代碼進行初始化。隻會執行一次。

<code>downgrade</code>: 檢測頁面降級配置進行頁面降級。

<code>initevents</code>: 綁定events和lifecycle(init、create、ready)執行的鈎子。

<code>initscope</code>: 執行initdata()、initcomputed、initmethods。初始化data、computed屬性和methods,并進行data的observer監聽。

<code>build</code>: 根據預留選項<code>opt.replace</code>進行編譯,目前該選項還未被實質使用。編譯完成後執行ready的鈎子指令,執行ready。

<code>compile</code>: 編譯視圖。

<code>updateactions</code>: 檢測是否有資料更新需要執行。

<code>createfinish</code>: 表明dom結建構立完成,想callqueue隊列中添加一個'createfinish'的actions。

<code>processcallqueue</code>: 依次執行隊列中的actions,進行節點渲染到頁面的過程,為了性能考慮,通過requestanimationframe進行分幀渲染。

callqueue隊列

淺析weex之vdom渲染前言一. 檔案結構二. 主要類分析三. 初始化過程四. 資料更新過程總結

通過初始化過程我們可以得到<code>init -&gt; 資料監聽 -&gt; created -&gt; 視圖生成 -&gt; ready</code>,為了避免重複的視圖操作,可在init進行資料的擷取,created階段進行資料的指派和修改。

淺析weex之vdom渲染前言一. 檔案結構二. 主要類分析三. 初始化過程四. 資料更新過程總結

從上圖可知,weex提供了兩種append方式:tree、node。

tree:先渲染子節點樹,最後渲染父節點

node:先渲染父節點,然後子節點一個個append

進行了不同的節點數量,vm建立耗時采樣對比,從圖中可以看出當節點個數較多的時候tree模式比node模式渲染的快

| 1個文本節點 | 第1次 | 第2次 | 第3次 | 第4次 | 第5次 | 平均 |

| ----- | ------ | ------- | ------- | ----- | ------ | ------- |

| tree | 8ms | 10ms| 12ms| 9ms| 10ms| 9.8ms|

| node | 10ms| 9ms| 9ms| 9ms| 9ms| 9.2ms|

| 20個文本節點 | 第1次 | 第2次 | 第3次 | 第4次 | 第5次 | 平均 |

| tree | 17ms | 18ms| 18ms| 16ms| 18ms| 17.4ms|

| node | 18ms| 17ms| 21ms| 17ms| 18ms| 18.2ms|

| 50個文本節點 | 第1次 | 第2次 | 第3次 | 第4次 | 第5次 | 平均 |

| tree | 32ms | 28ms| 26ms| 27ms| 27ms|28ms|

| node | 30ms| 29ms| 34ms| 33ms| 31ms| 31.4ms|

| 100個文本節點 | 第1次 | 第2次 | 第3次 | 第4次 | 第5次 | 平均 |

| tree | 44ms | 41ms| 37ms| 37ms| 44ms|40.6ms|

| node | 46ms| 44ms| 41ms| 44ms| 43ms| 43.6ms|

淺析weex之vdom渲染前言一. 檔案結構二. 主要類分析三. 初始化過程四. 資料更新過程總結

<code>attachtarget</code>: 進行節點渲染的時候,将每個append動作細化為具體的actions,置入callqueue隊列中。

<code>updateactions</code>: 檢測是否有diff,如果有,則執行diff中記錄的task

<code>calltasks</code>: 調用callnative,根據執行狀态判斷是否執行callqueue清單中的人物或者置入callqueue隊列中。

執行click事件,其中修改了data資料值,執行順序如下:

淺析weex之vdom渲染前言一. 檔案結構二. 主要類分析三. 初始化過程四. 資料更新過程總結

calljs響應事件、接受事件,通過eventmanager獲得事件目标響應函數并fire執行,通過watcher監聽資料修改,如果資料前後不等則将修改更新操作記入diff中,同時通知訂閱它的依賴繼續收集更新操作。最終執行updateactions完成資料更新操作。

通過上文分析,可以認為:

tree模式比node模式渲染的快。

每個節點的建立都對應一個callqueue任務,節點逐個逐個的append到頁面中。

依賴釋出訂閱模式收集依賴,監聽每一個屬性的變化,可直接擷取更新操作,映射到dom結構中。

與native互動通過 callnative()方法;響應js調用采用calljs()方法。

以上是個人拙見,本文中描述不正确的地方歡迎指正~

繼續閱讀