diff算法

diff算法的做用
計算出Virtual DOM中真正變化的部分,並只針對該部分進行原生DOM操做,而非從新渲染整個頁面。react

 

  傳統diff算法
  經過循環遞歸對節點進行依次對比,算法複雜度達到 O(n^3) ,n是樹的節點數,這個有多可怕呢?——若是要展現1000個節點,得執行上億次比較。。即使是CPU快能執行30億條命令,也 很難在一秒內計算出差別。
  react的diff算法
  diff算法是調和的具體實現。
    調和:將Virtual DOM樹轉換成actual DOM樹的最少操做的過程 稱爲 調和 。

  diff策略
    React用 三大策略 將O(n^3)複雜度 轉化爲 O(n)複雜度算法

  策略一(tree diff):
    Web UI中DOM節點跨層級的移動操做特別少,能夠忽略不計。性能

  策略二(component diff):
    擁有相同類的兩個組件 生成類似的樹形結構,
    擁有不一樣類的兩個組件 生成不一樣的樹形結構。優化

  策略三(element diff):
    對於同一層級的一組子節點,經過惟一id區分。spa

   tree diff算法
  (1)React經過updateDepth對Virtual DOM樹進行 層級控制
  (2)對樹分層比較,兩棵樹 只對 同一層次節點 進行比較。若是該節點不存在時,則該節點及其子節點會被徹底刪除,不會再進一步比較。
  (3)只需遍歷一次,就能完成整棵DOM樹的比較。
    

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

  只會對相同層級的 DOM 節點進行比較,當發現節點已經不存在時,則該節點及其子節點會被徹底刪除,不會用於進一步的比較。遞歸

  若是出現了 DOM 節點跨層級的移動操做。索引

  如上圖這樣,A節點就會被直接銷燬了。element

  Diif 的執行狀況是:create A -> create C -> create D -> delete A開發

 

  component diff
  React對不一樣的組件間的比較,有三種策略
  (1)同一類型的兩個組件,按原策略(層級比較)繼續比較Virtual DOM樹便可。

  (2)同一類型的兩個組件,組件A變化爲組件B時,可能Virtual DOM沒有任何變化,若是知道這點(變換的過程當中,Virtual DOM沒有改變),可節省大量計算時間,因此 用戶 能夠經過 shouldComponentUpdate() 來判斷是否須要 判斷計算。

  (3)不一樣類型的組件,將一個(將被改變的)組件判斷爲dirty component(髒組件),從而替換 整個組件的全部節點

  注意:若是組件D和組件G的結構類似,可是 React判斷是 不一樣類型的組件,則不會比較其結構,而是刪除 組件D及其子節點,建立組件G及其子節點。

 

  element diff
  當節點處於同一層級時,diff提供三種節點操做:刪除、插入、移動

  插入:組件 C 不在集合(A,B)中,須要插入

  刪除:(1)組件 D 在集合(A,B,D)中,但 D的節點已經更改,不能複用和更新,因此須要刪除 舊的 D ,再建立新的。

  (2)組件 D 以前在 集合(A,B,D)中,但集合變成新的集合(A,B)了,D 就須要被刪除。

  移動:組件D已經在集合(A,B,C,D)裏了,且集合更新時,D沒有發生更新,只是位置改變,如新集合(A,D,B,C),D在第二個,無須像傳統diff,讓舊集合的第二個B和新集合的第二個D 比較,而且刪除第二個位置的B,再在第二個位置插入D,而是 (對同一層級的同組子節點) 添加惟一key進行區分,移動便可。

 

  element diff
  當節點處於同一層級時,diff提供三種節點操做:刪除、插入、移動

  插入:組件 C 不在集合(A,B)中,須要插入

  刪除:(1)組件 D 在集合(A,B,D)中,但 D的節點已經更改,不能複用和更新,因此須要刪除 舊的 D ,再建立新的。

  (2)組件 D 以前在 集合(A,B,D)中,但集合變成新的集合(A,B)了,D 就須要被刪除。

  移動:組件D已經在集合(A,B,C,D)裏了,且集合更新時,D沒有發生更新,只是位置改變,如新集合(A,D,B,C),D在第二個,無須像傳統diff,讓舊集合的第二個B和新集合的第二個D 比較,而且刪除第二個位置的B,再在第二個位置插入D,而是 (對同一層級的同組子節點) 添加惟一key進行區分,移動便可。

 

  移動的條件:

  • 對新集合中的節點進行循環遍歷,新舊集合中是否存在相同節點

  • nextIndex: 新集合中當前節點的位置

  • lastIndex: 訪問過的節點在舊集合中最右的位置(最大位置)

  • If (child._mountIndex < lastIndex)

 

 

  情形一:新舊集合中存在相同節點但位置不一樣時,如何移動節點

  

  (1)看着上圖的 B,React先重新中取得B,而後判斷舊中是否存在相同節點B,當發現存在節點B後,就去判斷是否移動B。
  B在舊 中的index=1,它的lastIndex=0,不知足 index < lastIndex 的條件,所以 B 不作移動操做。此時,一個操做是,lastIndex=(index,lastIndex)中的較大數=1.

  注意:lastIndex有點像浮標,或者說是一個map的索引,一開始默認值是0,它會與map中的元素進行比較,比較完後,會改變本身的值的(取index和lastIndex的較大數)。

  (2)看着 A,A在舊的index=0,此時的lastIndex=1(由於先前與新的B比較過了),知足index<lastIndex,所以,對A進行移動操做,此時lastIndex=max(index,lastIndex)=1

  (3)看着D,同(1),不移動,因爲D在舊的index=3,比較時,lastIndex=1,因此改變lastIndex=max(index,lastIndex)=3

  (4)看着C,同(2),移動,C在舊的index=2,知足index<lastIndex(lastIndex=3),因此移動。

  因爲C已是最後一個節點,因此diff操做結束。

 

  情形二:新集合中有新加入的節點,舊集合中有刪除的節點

 

  (1)B不移動,不贅述,更新l astIndex=1

  (2)新集合取得 E,發現舊不存在,故在lastIndex=1的位置 建立E,更新lastIndex=1

  (3)新集合取得C,C不移動,更新lastIndex=2

  (4)新集合取得A,A移動,同上,更新lastIndex=2

  (5)新集合對比後,再對舊集合遍歷。判斷 新集合 沒有,但 舊集合 有的元素(如D,新集合沒有,舊集合有),發現 D,刪除D,diff操做結束。

 

  diff的不足與待優化的地方

 

 

  看圖的 D,此時D不移動,但它的index是最大的,致使更新lastIndex=3,從而使得其餘元素A,B,C的index<lastIndex,致使A,B,C都要去移動。

理想狀況是隻移動D,不移動A,B,C。所以,在開發過程當中,儘可能減小相似將最後一個節點移動到列表首部的操做,當節點數量過大或更新操做過於頻繁時,會影響React的渲染性能。

相關文章
相關標籤/搜索