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某個...