banner
SlhwSR

SlhwSR

热爱技术的一名全栈开发者
github
bilibili

差異

一、是什麼#

Vue一致,React通過引入Virtual DOM的概念,極大地避免無效的Dom操作,使我們的頁面的構建效率提到了極大的提升

diff算法就是更高效地通過對比新舊Virtual DOM來找出真正的Dom變化之處

傳統 diff 算法通過循環遞歸對節點進行依次對比,效率低下,算法複雜度達到 O (n^3),react將算法進行一個優化,複雜度將為O(n),兩者效率差距如下圖:

image

二、原理#

reactdiff算法主要遵循三個層級的策略:

  • tree 層級

  • conponent 層級

  • element 層級

tree 層級#

DOM節點跨層級的操作不做優化,只會對相同層級的節點進行比較

image

只有刪除、創建操作,沒有移動操作,如下圖:

image

react發現新樹中,R 節點下沒有了 A,那麼直接刪除 A,在 D 節點下創建 A 以及下屬節點

上述操作中,只有刪除和創建操作

conponent 層級#

如果是同一類的組件,則會繼續往下diff運算,如果不是一個類的組件,那麼直接刪除這個組件下的所有子節點,創建新的

image

component D 換成了component G 後,即使兩者的結構非常類似,也會將D刪除再重新創建G

element 層級#

對於比較同一層級的節點們,每個節點在對應的層級用唯一的key作為標識

提供了 3 種節點操作,分別為 INSERT_MARKUP (插入)、MOVE_EXISTING (移動) 和 REMOVE_NODE (刪除)

如下場景:

image

通過key可以準確地發現新舊集合中的節點都是相同的節點,因此無需進行節點刪除和創建,只需要將舊集合中節點的位置進行移動,更新為新集合中節點的位置

流程如下表:

image

  • index: 新集合的遍歷下標。
  • oldIndex:當前節點在老集合中的下標
  • maxIndex:在新集合訪問過的節點中,其在老集合的最大下標

如果當前節點在新集合中的位置比老集合中的位置靠前的話,是不會影響後續節點操作的,這裡這時候被動字節不用動

操作過程中只比較 oldIndex 和 maxIndex,規則如下:

  • 當 oldIndex>maxIndex 時,將 oldIndex 的值賦值給 maxIndex
  • 當 oldIndex=maxIndex 時,不操作
  • 當 oldIndex<maxIndex 時,將當前節點移動到 index 的位置

diff過程如下:

  • 節點 B:此時 maxIndex=0,oldIndex=1;滿足 maxIndex<oldIndex,因此 B 節點不動,此時 maxIndex= Math.max (oldIndex, maxIndex),就是 1
  • 節點 A:此時 maxIndex=1,oldIndex=0;不滿足 maxIndex<oldIndex,因此 A 節點進行移動操作,此時 maxIndex= Math.max (oldIndex, maxIndex),還是 1
  • 節點 D:此時 maxIndex=1, oldIndex=3;滿足 maxIndex<oldIndex,因此 D 節點不動,此時 maxIndex= Math.max (oldIndex, maxIndex),就是 3
  • 節點 C:此時 maxIndex=3,oldIndex=2;不滿足 maxIndex< oldIndex,因此 C 節點進行移動操作,當前已經比較完了

當 ABCD 節點比較完成後,diff過程還沒完,還會整體遍歷老集合中節點,看有沒有沒用到的節點,有的話,就刪除

三、注意事項#

對於簡單列表渲染而言,不使用key比使用key的性能,例如:

將一個 [1,2,3,4,5],渲染成如下的樣子:

<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>

後續更改成 [1,3,2,5,4],使用key與不使用key作用如下:

1.加key
<div key='1'>1</div>             <div key='1'>1</div>     
<div key='2'>2</div>             <div key='3'>3</div>  
<div key='3'>3</div>  ========>  <div key='2'>2</div>  
<div key='4'>4</div>             <div key='5'>5</div>  
<div key='5'>5</div>             <div key='4'>4</div>  
操作:節點2移動至下標為2的位置,節點4移動至下標為4的位置。

2.不加key
<div>1</div>             <div>1</div>     
<div>2</div>             <div>3</div>  
<div>3</div>  ========>  <div>2</div>  
<div>4</div>             <div>5</div>  
<div>5</div>             <div>4</div>  
操作:修改第1個到第5個節點的innerText

如果我們對這個集合進行增刪的操作改成 [1,3,2,5,6]

1.加key
<div key='1'>1</div>             <div key='1'>1</div>     
<div key='2'>2</div>             <div key='3'>3</div>  
<div key='3'>3</div>  ========>  <div key='2'>2</div>  
<div key='4'>4</div>             <div key='5'>5</div>  
<div key='5'>5</div>             <div key='6'>6</div>  
操作:節點2移動至下標為2的位置,新增節點6至下標為4的位置,刪除節點4。

2.不加key
<div>1</div>             <div>1</div>     
<div>2</div>             <div>3</div>  
<div>3</div>  ========>  <div>2</div>  
<div>4</div>             <div>5</div>  
<div>5</div>             <div>6</div> 
操作:修改第1個到第5個節點的innerText

由於dom節點的移動操作開銷是比較昂貴的,沒有key的情況下要比有key的性能更好。

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。