1. 程式人生 > >React學習之進階調解器(十八)

React學習之進階調解器(十八)

React提供給我們宣告式的API以至於我們根本不需要關心React內部到底做了什麼,這讓我們寫程式碼變得輕鬆,但是我們還是非常有必要了解React內部實現機制,這對我們自己開發一個公司框架以及深入學習React是非常有幫助的。

這一篇部落格就是深入的講解React的更新機制也屬於React的調解器知識點,開啟你內心世界React的大門,讓我們見識見識diff演算法的魅力吧,騷年們!

1.演算法魅力

之前將效能優化的時候已經提到過基本的更新流程,render進行檢視渲染,然後React檢查是否發生變化,發生了就更新。

有一些一般的演算法以最小的代價更新一棵樹為另外一棵樹,即便是最好的演算法它也是O

(n3)的時間複雜度,n為樹上的元素。(雖然個人並不知道它說的是什麼演算法啊,也不知道是怎麼實現的,但是官網中有pdf詳細進行了演算法講解,大家可以去看看,當然在React高階教程中會進行講解)

[這裡說的演算法只是說以最小的更新節點個數來定義的,也就是說上面描述的演算法求解的是將一棵樹變為另外一棵樹最少需要更改多少個節點,時間負責度是那麼多,實際應用中,我們不僅僅只是考慮以更新最好的節點來評判的,至少React的diff演算法處理這個問題上就不是。]

當我們在React用前面那種演算法的話,就單單1000個元素都需要O(10003)的複雜度,還玩個毛線。這種效率實在是太低,代價昂貴,所以React用一個種O

(n)的啟發式演算法來進行處理。

這個啟發式演算法的使用基於以下兩點:

  1. 只要兩個元素型別不同就會構造出不同的樹

  2. 開發者可以會給每一個元素標記一個特殊的雜湊屬性

2.Diff演算法

型別不同

在比較兩顆樹時,React首先比較這兩顆樹的根節點,至於判斷不同就是判斷他們的相應的狀態和資料了

如果兩個元素的型別不同的話,React會拋棄舊的樹,構造新的樹,比如說從<a>變到了<img><article>變到了<comment>,<button>變到了<div>,這些變化都會造成一個全新的樹的構建,記住是以當前節點為根,完全重新建立,而不是單單更新根節點。

當摧毀舊樹時,元件會呼叫componentWillUnmount()進行摧毀自身,當建立新的輸時,會呼叫componentWillMount()處理,接著就是componentDidMount()處理,這裡注意componentWillUnmountcomponentWillMount的區別

這種更新方式會導致子樹的所有節點狀態摧毀,重新進行建立

<div>
  <Counter />
</div>

<span>
  <Counter />
</span>

型別相同

當型別相同時,React會看他們的屬性,然後更新要更改的屬性

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

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

這樣,React只會去修改節點的className屬性,而不會去更改其他的地方

當處理完這一層節點後,React就會遞迴去處理它們的子節點

特提:處理型別相同的元件

當是比較型別相同的元件時,React呼叫componentWillReceivePropscomponentWillUpdate函式進行處理(這兩個函式後續會講解),然後直接使用render函式,接著又是diff演算法處理

3.對於列表孩子的處理

每當他們的父親節點不同時,React就會處理出孩子列表,然後進行更新。

<ul>
  <li>first</li>
  <li>second</li>
</ul>

<ul>
  <li>first</li>
  <li>second</li>
  <li>third</li>
</ul>

當時如果我們只是在孩子節點後面直接插入一個<li>chird</li>,它只會直接處理。
可是請看下面例子

<ul>
  <li>Duke</li>
  <li>Villanova</li>
</ul>

<ul>
  <li>Connecticut</li>
  <li>Duke</li>
  <li>Villanova</li>
</ul>

這會更新所有的孩子節點,效率就比較低了

4.keys優化

為了解決3最後更新所有孩子節點的問題,React就提供了一個key屬性,這個屬性,之前將列表的時候就說過了,維護一個key比維護一個li標籤更好。

<ul>
  <li key="2015">Duke</li>
  <li key="2016">Villanova</li>
</ul>

<ul>
  <li key="2014">Connecticut</li>
  <li key="2015">Duke</li>
  <li key="2016">Villanova</li>
</ul>

這樣React直接就知道key=2014是一個新的元素,而key=2015key=2016則會往後移。

這其中的key要是唯一的,你可以是物件自己提供的,也可以用陣列的索引,優劣在列表章節已經說了。

5.權衡

瞭解演算法的實現是非常重要的,這樣我們就可以更加深入的瞭解React處理的每一個細節,從而可以去優化效能.

當然,處理事件的時候一般會遇到這麼兩個問題

  1. 如果兩者相似,應該作為不同處理,構造出不同的樹

  2. key值要具有唯一性,否則會造成子元件的丟失

這兩個就是上面說的啟發式處理的前提條件。

下一篇將講React中的資料