react虛擬dom機制與diff演算法

2021-08-25 22:07:38 字數 3429 閱讀 3810

react的乙個突出特點是擁有極速地渲染效能。該功能依靠的就是facebook研發團隊弄出的虛擬dom機制以及其獨特的diff演算法。下面簡單解釋一下react虛擬dom機制和diff演算法的實現思想:

要講虛擬dom機制必須提到乙個概念——虛擬dom樹,這是react在真實dom樹基礎上建立的乙個抽象的樹,應用、虛擬dom與真實dom的關係如下圖顯示:

而標準的dom機制如下圖所示:

對比兩個圖就可以發現標準dom機制下,使用者在應用上的操作是直接對真實dom進行操作的,而在react應用中,使用者在應用中對dom的操作其實是對虛擬dom的操作,使用者的操作產生的資料改變或者state變數改變(此處的改變具體的講就是事件函式對dom的操作)都會儲存到虛擬dom上,之後再批量的對這些更改進行diff演算法計算,對比操作前後的虛擬dom樹,把更改後的變化再同步到真實dom上。舉個例子:

標準dom機制下對某一節點在事件函式中做如下操作:

var a=document.getelementbyid('test');

a.style.backgroundcolor = "black"

;a.style.backgroundcolor = "red"

;a.style.backgroundcolor = "black"

;

如上所示,在標準dom機制下,會對a節點進行三次的dom操作。

而在react應用的事件函式中進行如上操作時,同樣會在虛擬dom上進行三次dom的操作,但在真實dom中,它只會執行一次dom操作,即a.style.backgroundcolor = 「black」;因為在react虛擬dom機制中,它會把所有的操作都會合併,只會對比剛開始的狀態和最後操作的狀態,兩者中找出不同再同步到真實dom中,這就大大減少了真實dom的操作,而眾所周知,dom操作是很耗效能的,這是react能做到極速渲染的原因之一。

另外乙個原因就是react獨特的diff演算法,同樣給出標準diff演算法和react diff演算法的描述,對比了就會明白了:

首先講一下diff演算法的處理方法,對操作前後的dom樹同一層的節點進行對比,一層一層對比,如下圖:

在標準dom機制下在同一位置對比前後的dom節點,發現節點改變了,會繼續比較該節點的子節點,一層層對比,找到不同的節點,然後更新節點。

在react的diff演算法下,在同一位置對比前後dom節點,只要發現不同,就會刪除操作前的domm節點(包括其子節點),替換為操作後的dom節點。

對比兩種diff演算法,大家可以發現,react的diff演算法下,當dom節點更改時,會大大減少dom樹的節點遍歷,這也是其另外乙個可以實現極速渲染的乙個原因。

diff策略

web ui 中dom節點跨層級的移動操作特別少,可以忽略不計

擁有相同類的兩個元件將會生成相似的樹形結構,擁有不同類的兩個元件將會生成不同的樹形結構。

對於同一層級的一組子節點,它們可以通過唯一id進行區分。

對於以上三個策略,react分別對tree diff,component diff,element diff進行演算法優化。

1.tree diff

基於策略一,webui中dom節點跨層級的移動操作少的可以忽略不計,react對virtual dom樹進行層級控制,只會對相同層級的dom節點進行比較,即同乙個父元素下的所有子節點,當發現節點已經不存在了,則會刪除掉該節點下所有的子節點,不會再進行比較。這樣只需要對dom樹進行一次遍歷,就可以完成整個樹的比較。複雜度變為o(n);

疑問:當我們的dom節點進行跨層級操作時,diff會有怎麼樣的表現呢?

如下圖所示,a節點及其子節點被整個移動到d節點下面去,由於react只會簡單的考慮同級節點的位置變換,而對於不同層級的節點,只有建立和刪除操作,所以當根節點發現a節點消失了,就會刪除a節點及其子節點,當d發現多了乙個子節點a,就會建立新的a作為其子節點。

由此可以發現,當出現節點跨層級移動時,並不會出現想象中的移動操作,而是會進行刪除,重新建立的動作,這是一種很影響react效能的操作。因此官方也不建議進行dom節點跨層級的操作。

2.componnet diff

react是基於元件構建應用的,對於元件間的比較所採用的策略也是非常簡潔和高效的。

如果是同乙個型別的元件,則按照原策略進行virtual dom比較。

如果不是同一型別的元件,則將其判斷為dirty component,從而替換整個組價下的所有子節點。

如果是同乙個型別的元件,有可能經過一輪virtual dom比較下來,並沒有發生變化。如果我們能夠提前確切知道這一點,那麼就可以省下大量的diff運算時間。因此,react允許使用者通過shouldcomponentupdate()來判斷該元件是否需要進行diff演算法分析。

當元件d變為元件g時,即使這兩個元件結構相似,一旦react判斷d和g是不用型別的元件,就不會比較兩者的結構,而是直接刪除元件d,重新建立元件g及其子節點。雖然當兩個元件是不同型別但結構相似時,進行diff演算法分析會影響效能,但是畢竟不同型別的元件存在相似dom樹的情況在實際開發過程中很少出現,因此這種極端因素很難在實際開發過程中造成重大影響。

3.element diff

當節點屬於同一層級時,diff提供了3種節點操作,分別為insert_markup(插入),move_existing(移動),remove_node(刪除)。

insert_markup:新的元件型別不在舊集合中,即全新的節點,需要對新節點進行插入操作。

move_existing:舊集合中有新元件型別,且element是可更新的型別,這時候就需要做移動操作,可以復用以前的dom節點。

remove_node:舊元件型別,在新集合裡也有,但對應的element不同則不能直接復用和更新,需要執行刪除操作,或者舊元件不在新集合裡的,也需要執行刪除操作。

React虛擬DOM轉換為真實DOM

按照上面的猜想,那麼render方法的作用就是 render方法接收兩個引數,第二個引數沒什麼好說的,固定寫法,第乙個引數有以下幾種情況 類元件函式元件 文字 數字 class extends component 然後再來處理函式元件,函式元件的寫法是,直接就返回了dom 對於文字 數字,直接新增到...

React 中的虛擬DOM

react 的重新渲染,效能是很高的。因為它引入了虛擬dom的概念。呃,來看一下,render 函式渲染頁面的幾種做法。前兩步都是拿到state 資料 與 jsx模版。第一種就是比較樸素的方式。第二種方式雖然不用完全替換,但是也需要比對兩個dom。第三種是虛擬dom方式。虛擬dom 本質上就是 js...

React 重溫之 虛擬DOM

虛擬dom可以說是現代前端庫的標配了,好像你乙個前端框架不實現乙個虛擬dom出門都不好意思跟人打招呼,那麼到底什麼是虛擬dom呢?那麼與之相對應的,我們可以簡單理解成虛擬dom就是用其他語言模擬出乙個dom的樹形結構 那麼問題來了,為什麼要用其它語言模擬dom,人家本來就已經是乙個很完整的樹形結構物...