diff演算法 diff演算法介紹

2021-10-11 07:54:54 字數 3834 閱讀 1664

diff演算法的作用

計算出virtual dom中真正變化的部分,並只針對該部分進行原生dom操作,而非重新渲染整個頁面。

傳統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區分。

tree diff演算法

(1)react通過updatedepth對virtual dom樹進行層級控制

(2)對樹分層比較,兩棵樹 只對同一層次節點進行比較。如果該節點不存在時,則該節點及其子節點會被完全刪除,不會再進一步比較。

(3)只需遍歷一次,就能完成整棵dom樹的比較。

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

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

如果出現了 dom 節點跨層級的移動操作。

如上圖這樣,a節點就會被直接銷毀了。

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進行區分,移動即可。

移動的條件:

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

(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 ,因此,對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

由於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

理想情況是只移動d,不移動a,b,c。因此,在開發過程中,儘量減少類似將最後乙個節點移動到列表首部的操作,當節點數量過大或更新操作過於頻繁時,會影響react的渲染效能。

Diff演算法研究

在unix linux的世界裡面,如果我們需要比較兩個檔案,就會用乙個比較的命令 diff。而這個diff的原理是什麼呢?在diff裡面,我們比較的兩個檔案叫做old和new,而一般是按行來比較。這裡我們可以抽象成乙個字串的比較,比如 old abcdefger new abdefereger 那麼...

diff程式的演算法

diff程式很重要,linux中的源 補丁都是diff作出來的,diff在比較兩個文字檔案的不同方面很高效,它是基於行的,diff會將兩個檔案都按照行分成若干部分,然後計算這些行每一行的校驗碼,之後的問題就是比較這兩個檔案的所有行的校驗碼序列的不同了,這就把問題歸結為了序列比對,diff用的是動態規...

vue的diff演算法

1.當資料發生變化時,vue是怎麼更新節點的?渲染真實dom的開銷很大,比如我們修改了某個資料,如果直接渲染到真實dom會引起整個dom樹的重繪和重排,有沒有可能我們只更新我們修改的那一小塊dom而不更新整個dom呢?diff演算法可以幫助我們。我們根據真實dom生成乙個虛擬dom,當虛擬dom某個...