React高階系列 虛擬dom與diff演算法

2021-09-13 03:45:04 字數 4072 閱讀 5243

jsx 表面寫的是html,其實內部執行的是一段js

createelement

react.createelement(

type,

[props],

[...children]

)

createelement把這個樹形結構,存在記憶體裡面

jsx最終以這樣的乙個個物件遞迴的存在記憶體中,執行diff演算法

多層結構

簡單的createelement實現

reactelement - 生成的是乙個物件來描述這個節點

與傳統樹的diff的區別

計算一棵樹形結構轉換成另一棵樹形結構的最少操作,是乙個複雜且值得研究的問題。傳統 diff 演算法通過迴圈遞迴對節點進行依次對比,效率低下,演算法複雜度達到 o(n^3)

react diff策略

tree diff

基於策略一,對樹進行分層比較,兩棵樹只會對同一層次的節點進行比較。  

react 通過 updatedepth 對 virtual dom 樹進行層級控制,同乙個父節點下的所有子節點。

什麼是 dom 節點跨層級的移動操作?

a 節點(包括其子節點)整個被移動到 d 節點下

如果出現了 dom 節點跨層級的移動操作,react diff 會有怎樣的表現呢?

react 只會簡單的考慮同層級節點的位置變換,而對於不同層級的節點,只有建立和刪除操作。
當根節點發現子節點中 a 消失了,就會直接銷毀 a;當 d 發現多了乙個子節點 a,則會建立新的 a(包括子節點)作為其子節點。此時,react diff 的執**況:create a -> create b -> create c -> delete a。

注意:

在開發元件時,保持穩定的 dom 結構會有助於效能的提公升。例如,可以通過 css 隱藏或顯示節點,而不是真的移除或新增 dom 節點。
component diff依據策略二

react 判斷 d 和 g 是不同型別的元件,就不會比較二者的結構,而是直接刪除 component d,重新建立 component g 以及其子節點,即使d 和 g的結構很相似

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

eg: 新老集合進行 diff 差異化對比,發現 b != a,則建立並插入 b 至新集合,刪除老集合 a;以此類推,建立並插入 a、d 和 c,刪除 b、c 和 d。

帶來的問題:都是相同的節點,但由於位置發生變化,導致需要進行繁雜低效的刪除、建立操作,其實只要對這些節點進行位置移動即可

react優化策略:允許開發者對同一層級的同組子節點,新增唯一 key 進行區分

優化後diff實現:

對新集合的節點進行迴圈遍歷,通過唯一 key 可以判斷新老集合中是否存在相同的節點

如果存在相同節點,則進行移動操作,但在移動前需要將當前節點在老集合中的位置child._mountindex與lastindex(訪問過的節點在老集合中最右的位置即最大的位置)進行比較,if (child._mountindex < lastindex),則進行節點移動操作

分析:

element  _mountindex  lastindex  nextindex  enqueuemove

b 1 0 0 false

a 0 1 1 true

d 3 1 2 false

c 2 3 3 true

step:

從新集合中取得 b,判斷老集合中存在相同節點 b

b 在老集合中的位置 b._mountindex = 1

初始 lastindex = 0

不滿足 child._mountindex < lastindex 的條件,因此不對 b 進行移動操作

更新 lastindex = math.max(prevchild._mountindex, lastindex) lastindex更新為1

將 b 的位置更新為新集合中的位置prevchild._mountindex = nextindex,此時新集合中 b._mountindex = 0,nextindex++

以上主要分析新老集合中存在相同節點但位置不同時,對節點進行位置移動的情況,如果新集合中有新加入的節點且老集合存在需要刪除的節點,那麼 react diff 又是如何對比運作的呢?

新建:從新集合中取得 e,判斷老集合中不存在相同節點 e,則建立新節點 e

lastindex不做處理

e 的位置更新為新集合中的位置,nextindex++

刪除:當完成新集合中所有節點 diff 時,最後還需要對老集合進行迴圈遍歷,判斷是否存在新集合中沒有但老集合中仍存在的節點,發現存在這樣的節點 d,因此刪除節點 d

react diff的問題

理論上 diff 應該只需對 d 執行移動操作,然而由於 d 在老集合的位置是最大的,導致其他節點的 _mountindex < lastindex,造成 d 沒有執行移動操作,而是 a、b、c 全部移動到 d 節點後面的現象

建議:在開發過程中,儘量減少類似將最後乙個節點移動到列表首部的操作,當節點數量過大或更新操作過於頻繁時,在一定程度上會影響 react 的渲染效能。
總結:

react虛擬dom機制與diff演算法

react的乙個突出特點是擁有極速地渲染效能。該功能依靠的就是facebook研發團隊弄出的虛擬dom機制以及其獨特的diff演算法。下面簡單解釋一下react虛擬dom機制和diff演算法的實現思想 要講虛擬dom機制必須提到乙個概念 虛擬dom樹,這是react在真實dom樹基礎上建立的乙個抽象...

React虛擬DOM轉換為真實DOM

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

React 中的虛擬DOM

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