咱們都知道使用React可使得網頁的性能有很大的提升,本文具體探究它是經過什麼樣的渲染機制作到的。html
在頁面一開始打開的時候,React會調用render函數構建一棵Dom樹,在state/props發生改變的時候,render函數會被再次調用渲染出另一棵樹,接着,React會用對兩棵樹進行對比,找到須要更新的地方批量改動。react
這個過程當中,比較兩棵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的頻繁操做,從而提高的性能。
可是,是否是真的須要對全部的節點都從新渲染一遍呢?
下一篇文章,咱們將繼續探討這個問題~
參考文檔: