1. 程式人生 > >react 渲染機制(Reconciliation)

react 渲染機制(Reconciliation)

React渲染過程

我們都知道使用React可以使得網頁的效能有很大的提高,本文具體探究它是通過什麼樣的渲染機制做到的。

在頁面一開始開啟的時候,React會呼叫render函式構建當前頁面的一棵Dom樹,在state/props發生改變的時候,render函式會被再次呼叫渲染出另外一棵樹,接著,React會用對兩棵樹進行對比,找到需要更新的地方批量改動

Diff 演算法

這個過程中,比較兩棵Dom tree高效找出需要更新的地方是很重要的。React基於兩個假設:

  • 兩個相同的節點(element)產生類似的DOM結構,不同節點(element)產生不同的DOM結構

  • 對於同一層次的一組子節點,它們可以通過唯一的key進行區分

發明了一種叫Diff的演算法,它極大的優化了這個比較的過程,將演算法複雜度從O(n^3)降低到O(n)。

同時,基於第一點假設,我們可以推論出,Diff演算法只會對同層的節點進行比較。

也就是說如果父節點不同,React將不會在去對比子節點,直接解除安裝或刪除當前結點和其的直接點,然後重新構建。

下面,我們具體看下Diff演算法是怎麼做的,這裡分為五種情況考慮

  • 不同的節點型別
  • 相同節點型別當中的DOM節點
  • 相同型別當中的元件節點
  • DOM節點的遞迴children
  • 列表比較

不同的節點型別

當前遍歷到的兩個節點(一個是old tree, 一個是new tree)型別不同的時候,React將會解除安裝,該節點及節點下面的子節點所構成的樹,重新構建一個新的tree。

如果當前是old tree的DOM節點,那麼DOM節點會被直接destroy,。如果有children節點,那麼children的destroy方式根據節點型別的不同而不同(DOM節點或者是Component)。

如果當前是old tree的Component節點,那麼該Component會被解除安裝,執行componentWillUnmount這個方法,並且state會被丟失。構建Component的時候,是依次執行componentWillMount 、componentDidMount。 如果有children節點,那麼children的destroy方式根據節點型別的不同而不同(DOM節點或者是Component)。

相同節點型別當中的DOM節點

如果兩個型別相同的DOM節點,React根據attributes的不同然後去更新相應的屬性或者其attributes的值

相同型別當中的元件節點

當一個元件更新的時候,由於元件保持不變,所以state不會丟失,React會一次執行componentWillReceiveProps和componentWillUpdate,然後render方法會被呼叫並且diff演算法會對前一個結果後新的結果進行遞迴

DOM節點的遞迴children

需要注意的是對於同一個節點下面的同一層的子節點,需要保證key的唯一

列表比較

<div>
  <A />
  <B />
</div>
// 列表一到列表二
<div>
  <A />
  <C />
  <B />
</div>

從列表一到列表二,只是在中間插入了一個C,但是如果沒有key的時候,react會把B刪去,新建一個C放在B的位置,然後重新建一個節點B放在尾部。

從列表一到列表二的生命週期如下

列表一:
A is created
A render
B is created
B render
A componentDidMount
B componentDidMount

列表二:
A render
B componentWillUnmount
C is created
C render
B is created
B render
A componentDidUpdate
C componentDidMount
B componentDidMount

當節點很多的時候,這樣做是非常低效的,所以我們需要給每個節點配一個key,讓react可以識別出來哪些節點是一樣的,不需要重新建立。
配上key之後,在跑一遍程式碼看看

A render
C is created
C render
B render
A componentDidUpdate
C componentDidMount
B componentDidUpdate

果然,配上key之後,列表二的生命週期就如我所願,只在指定的位置建立C節點插入。
這裡要注意的一點是,key值必須是穩定(所以我們不能用Math.random()去建立key),可預測,並且唯一的。