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