React 渲染機制解析

React渲染過程

咱們都知道使用React可使得網頁的性能有很大的提升,本文具體探究它是經過什麼樣的渲染機制作到的。html

在頁面一開始打開的時候,React會調用render函數構建一棵Dom樹,在state/props發生改變的時候,render函數會被再次調用渲染出另一棵樹,接着,React會用對兩棵樹進行對比,找到須要更新的地方批量改動。react

Diff 算法

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

  • 兩個相同的組件產生相似的DOM結構,不一樣組件產生不一樣DOM結構github

  • 對於同一層次的一組子節點,它們能夠經過惟一的id區分算法

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

同時,基於第一點假設,咱們能夠推論出,Diff算法只會對同層的節點進行比較。如圖,它只會對顏色相同的節點進行比較。dom

也就是說若是父節點不一樣,React將不會在去對比子節點。由於不一樣的組件DOM結構會不相同,因此就沒有必要在去對比子節點了。這也提升了對比的效率。函數

下面,咱們具體看下Diff算法是怎麼作的,這裏分爲兩種狀況考慮性能

  • 節點類型不一樣優化

  • 節點類型相同,可是屬性不一樣

不一樣節點類型

對於不一樣的節點類型,react會基於第一條假設,直接刪去舊的節點,新建一個新的節點。

好比:

<A>
  <C/>
</A>
// 由shape1到shape2
<B>
  <C/>
</B>

React會直接刪掉A節點(包括它全部的子節點),而後新建一個B節點插入。

爲了驗證這一點,我打印出了從shape1到shape2節點的生命週期,gitbub連接:
https://github.com/hhhuangqio...

感興趣的能夠本身跑一跑代碼~

Shape1 :
A is created 
A render
C is created
C render
C componentDidMount
A componentDidMount

Shape2 :
A componentWillUnmount
C componentWillUnmount
B is created
B render
C is created
C render
C componentDidMount
B componentDidMount

由此能夠看出,A與其子節點C會被直接刪除,而後從新建一個B,C插入。

相同節點類型

當對比相同的節點類型比較簡單,react會對比它們的屬性,只改變須要改變的屬性

好比:

<div className="before" title="stuff" />

<div className="after" title="stuff" />

這兩個div中,react會只更新className的值

<div style={{color: 'red', fontWeight: 'bold'}} />

<div style={{color: 'green', fontWeight: 'bold'}} />

這兩個div中,react只會去更新color的值

列表比較

<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),可預測,而且惟一的。

小結

React整個的渲染機制就是在state/props發生改變的時候,從新渲染全部的節點,構造出新的虛擬Dom tree跟原來的Dom tree用Diff算法進行比較,獲得須要更新的地方在批量造做在真實的Dom上,因爲這樣作就減小了對Dom的頻繁操做,從而提高的性能。

探索性能優化

可是,是否是真的須要對全部的節點都從新渲染一遍呢?
下一篇文章,咱們將繼續探討這個問題~

參考文檔:

http://www.infoq.com/cn/artic...

https://facebook.github.io/re...

相關文章
相關標籤/搜索