使用React或者RN開發APP如果不知道Diff算法的話簡直是說不過去啊。畢竟“知其然,知其所以然”這句老話從遠古喊到現代了。
以下內容基本是官網文章的一個總結、壓縮。這次要謙虛一下,畢竟深入研究RN的時間不多,如果有什么理解的不對的地方還請各位讀者指正。
React的組件在渲染之后組成了一個樹形結構。在React繪制的時候,會在內存里對應每一個組件建立一個節點,并最終形成一個和組件樹結構一樣的樹。我們就叫這個樹叫影子樹(這個叫法不是出自官方)。我們可以理解為這個影子樹包含了React App組建的結構和一些屬性值。
在組件發生變化的時候(一般是調用了setState
),React會形成一個影子樹二號。然后對比影子樹1號和影子樹2號的不同。
我們知道對比兩個樹的最小不同的時間復雜度是O(n3
),n是樹里的節點數。這個復雜度下,稍有量級的應用都會遇到一個問題:無法忽略的慢。于是,FB的同學們使用了更加高效的啟發式算法,把復雜度降低到了O(n)。
但是,不管是什么算法最后都需要對比兩個節點的不同。有三種情況需要考慮:
一、節點之間的比較
節點,英語里的Node,包括兩種類型:一個是React組件,一個是HTML的DOM。下文也是同樣的含義。
節點類型不同
如果是HTML DOM不同的話,直接使用新的替換舊的。
如果是組件類型不同的話也直接使用新的替換舊的。
HTML DOM類型相同
在React里樣式并不是一個純粹的字符串,而是一個對象,這樣的話在樣式發生改變的時候只需要改變替換變化以后的樣式。修改完當前節點之后,遞歸處理該節點的子節點。
組件類型相同
組件類型相同的,使用React機制處理。一般是使用新的props替換掉舊的props,并在之后調用組件的componentWill/DidReceiveProps
方法,之前的組件的render方法會被調用。節點的比較機制開始遞歸作用于這個它的子節點上。
二、兩個列表之間的比較
一列節點中的一個發生了改變,React并沒有什么好方法來處理這個問題。循環新舊兩個列表,并找出不同是React唯一的處理方法。
但是,有一個可以把這個算法的復雜度降低的辦法。那就是我們在生成一列節點的時候給每一個節點上添加一個key。這個key只需要在這一列節點中唯一,不需要全局唯一。
三、取舍
需要注意的是,上面的啟發式算法是基于兩點假設:
*類型想聽的節點總是生成同樣的樹,而類型不同的節點也總是生成不同的樹。
*可以為多次render都表現穩定的節點設置key。
上面的節點之間的比較算法基本就是基于這兩個假設而實現的。也就是要提高React應用的效率,需要我們按照這兩點假設來開發。
否則,React會重獲整個APP。那就是噩夢一樣的情況了。