導航:首頁 > 源碼編譯 > diff演算法原理是什麼

diff演算法原理是什麼

發布時間:2023-07-20 04:05:04

① Diff演算法

Diff演算法的作用是用來計算出 Virtual DOM 中被改變的部分,然後針對該部分進行原生DOM操作,而不用重新渲染整個頁面。
Diff演算法有三大策略:

三種策略的執行順序也是順序依次執行。
Tree Diff 是對樹每一層進行遍歷,找出不同,如圖1所示。

Component Diff 是數據層面的差異比較

Element Diff 真實DOM渲染,結構差異的比較

首先進行第一層比較,第一層都是R,不發生變化;然後進入第二層Component Diff,發現A組件沒有,則刪除A及其子組件B、C;最後比較第三層,創建A及其子組件B、C。

當節點處於同一層級時,Diff提供三種DOM操作: 刪除 移動 插入

如圖2所示,首先將OldVnode 和 NewVnode的首尾位置分別標記為oldS、oldE、newS、newE。

(1) oldS和newS相同,不發生變化,oldS++,newS++。

(2) newS與OldVnode不匹配,oldS前面插入f,newS++。

(3) newS與oldE相同,oldE移動到oldS前面,newS++,oldE--。

(4) newE與oldE相同,不發生變化,newE--,oldE--。

(5) 都不相同,oldS前插入newE,刪除oldS,oldS++,newS++,newE--,oldE--。

(6) oldS > oldE,Diff結束,最後結果為:a、f、d、e、c。

最後附上核心源碼分析:
patch

這個函數做了以下事情:

updateChildren

上一篇 虛擬DOM : https://www.jianshu.com/p/580157c93c53

② React diff演算法

react 作為一款最主流的前端框架之一,在設計的時候除了簡化操作之外,最注重的地方就是節省性能了。diff演算法就是為 節省性能 而設計的,diff演算法和虛擬DOM的完美結合是react最有魅力的地方。其中,diff 是 different 的簡寫,這樣一來,diff 演算法是什麼也就顧名思義了——找不同。

在DOM需要更新的時候,通過diff演算法可以 計算出 虛擬DOM 中真正變化的部分,從而只針對變化的部分進行更新渲染,避免」牽一發而動全身「,造成性能浪費。

雖然完美地實現了找不同的功能,但是傻瓜式的 循環遞歸對節點進行依次的對比 ,使其演算法的時間復雜度為 O(n^3) ,其中n是dom樹的節點數。如果dom數足夠大的話,這個演算法將對cpu形成絕殺。

為了優化diff演算法,react中對普通的diff演算法實行了三大策略,成功將時間復雜度降為 O(n)

tree diff 是diff演算法的基礎策略,它的重點在於 同層比較

出於對diff演算法的優化,react的tree diff對DOM節點的跨層級移動的操作忽略不計,react對Virtual DOM樹進行層級控制,也就是說 只對相同層級的DOM節點進行比較 (即同一個父節點下的所有子節點)。對比時,一旦發現節點不存在,則直接刪除掉該節點以及之下的所有子節點。這樣秩序對DOM樹進行依次遍歷,就可以完成整個樹的對比。時間復雜度為O(n)

一個疑問:既然tree diff忽略了跨層級移動的操作,如果這種情況出現了,diff演算法會怎麼處理呢?

答:不管,多了就新增,少了就刪除( 只有創建節點和刪除節點的操作 )。所以官方明確建議不要進行DOM節點的跨層級操作。

component diff是組件間的對比

在遇到組件之間的比較時,有三種策略

優化點:

element diff 是針對同一層級的element節點的

在雙方同一層級的節點對比時,有三種情況

子節點更新時,會出現以下幾種情況:

react中的key值,它不是給開發者使用的。在一般情況下key值是當我們在做子元素遍歷的時候需要使用的。因為我們如果要進行數據的更新,就需要進行虛擬dom的對比,而key值就是每個元素節點對應的唯一值。這個時候就需要對比子元素的key值是否有匹配項,如果有的情況下則會進行數據的更新;如果key值沒有匹配項,那麼這個節點就需要進行刪除和重新創建。

因此我們在遍歷的時候千萬不要用 index下標 或者 時間戳、隨機數 等進行key值的賦值。這樣會造成元素的移除重新創建浪費性能。

③ React的diff演算法詳解

一、什麼是diff演算法?

為了增強用戶體驗,React從版本16開始將 同步更新 重構成了 可中斷的非同步更新 ,即採用了新的Reconciler(協調器,用於找出變化的組件),而新的Reconciler中採用了fiber架構。fiber架構的原理在此不再詳細解釋,我們目前只需要知道fiber節點可以保存dom信息,fiber節點構成的樹叫fiber樹,而更新dom是要用到『雙緩存技術』,即比較舊的fiber樹與此次要渲染的jsx對象,返回新的fiber樹進行渲染。 在舊fiber樹與jsx對象比較時,決定哪些節點要復用的過程,就是diff演算法

由於diff本身也會帶來性能消耗,為了降低演算法復雜度,React對diff做了 三個預設限制

更新後

如果沒有key會走第二條限制,有了key,react就可以判斷div和p節點是存在的,可以復用,只需要交換順序。

diff演算法會根據不同的jsx對象執行不同的處理函數,根據jsx對象的不同,我們可以分為兩類

1.JSX對象(之後都用newChildren表示)的類型為object、number、string,代表同級只有一個節點
2. newChildren的類型為Array,代表同級有多個節點。

二、單節點diff

對於單節點diff,用一個流程圖就可以解釋

更新後

由於 key的默認值為null ,所以更新前與更新後滿足key相同且元素類型不同,那麼我們要刪除更新前的三個div節點,新增p節點

三、多節點diff

對於多節點diff, 我們要 遍歷newChildren和oldFiber 進行比較。由於React團隊發現dom節點一般有更新,增加,刪除這三種操作,而更新更為頻繁,所以他們設置更新的優先順序高於增加刪除。基於以上原因,在多節點diff演算法的實現中有兩層遍歷, 第一層遍歷處理更新的節點,第二層遍歷處理更新以外的節點

第一層遍歷

遍歷newChildren與oldFiber, 判斷節點是否可復用,如果可以復用,則繼續遍歷。
如果不能復用,分為兩種情況:

第二層遍歷

第二層遍歷從第一層遍歷的結束位開始
第一層遍歷結束後有4種結果

首先我們要判斷newChildren中遍歷到的節點,在oldFiber中是否存在,基於此,React將oldFiber中的節點以key-oldfiber 鍵值對的形式存在Map中,只需要newChildren的key,就可以判斷oldFiber中有沒有相應的節點。

如果oldFiber中沒有相應的節點,則將newChildren生成的fiber打上placement標記

如果有相應的節點,將它的索引記為oldIndex,與上一次可復用節點在oldFiber的索引位置lastPlacedIndex比較,如果每次可復用的節點在上一次可復用右邊就說明位置沒有變化 ,即

oldIndex >=lastPlacedIndex, 說明相對位置沒有變化 ,那麼令lastPlacedIndex=oldIndex
oldIndex<lastPlacedIndex, 代表本節點需要向右移動
例如:

參考文檔
React技術揭秘 (iamkasong.com)

④ 徹底理解vue的patch流程和diff演算法

上一篇 《vue非同步更新流程梳理》 梳理了數據從賦值到更新到視圖的整體流程。但是最後的步驟 vm._update(vm._render()) 只是粗略的提了一嘴,現在就仔細的研究它內部的細節,搞清楚patch流程和diff原理是我們看源碼的重中之重。

當更新數據的時候就會執行這個updateComponent方法,即方法裡面的 vm._update(vm._render()) ,vm.render() 得到一個vnode,那麼vm._update到底干什麼? 進去看看

至此,無論是初始化還是更新都是靠patch來完成的 ,我們只需要看update流程就可以了。進入patch內部

patch函數主要接收oldVnode 與 vnode兩個參數,其實就是新舊兩棵虛擬樹。這里經過判斷條件 !isRealElement && sameVnode(oldVnode, vnode),不是真實節點 且是相同的vnode,進入patchVnode(oldVnode, vnode, insertedVnodeQueue, null, null, removeOnly); 我們只要關注oldVnode, vnode這兩個參數即可。

按照我們的例子,此時的oldVnode 與 vnode分別是

此處只列出關鍵屬性tag, key, elm, children,elm,還有很多其他的屬性沒有列出。真實的虛擬樹節點應該是如下圖

我們能看出 兩個vnode之間就是children[0]的不同:

追蹤流程發現,我們進入oldVnode 與 vnode的children進行對比,在updateChildren函數中。

我們先不去看updateChildren的邏輯,繼續看patchVnode這個函數其他的邏輯分支,得出 oldVnode 與 vnode的對比流程

總結: patchVnode這個方法的主要作用是對比兩個虛擬節點過程中去更新真實dom

接下來我們進入updateChildren流程,這是兩個children的對比,看一下這個函數的定義

函數解讀:

下面是兩個數組進行diff的流程,也就是diff演算法

diff解讀:
新舊兩個數組,都有雙端指針,兩端指針向中間靠攏,直到某個數組的兩端指針相交則退出循環。

在這個過程中,會先判斷是否有以下四種情況

如果不符合這4種情況,那就基於舊數組遍歷一次,拿到每個節點的key和index,就是oldKeyToIdx: {key1: 0, key2: 1}這種情況。然後去新數組首個節點開始匹配,匹配到就進行遞歸patchVnode流程,沒匹配到就進行創建新節點,插入到真實dom節點裡面去。

當循環結束,此時要麼是舊數組相交,要麼是新數組相交,只有這兩種情況:

至此diff流程結束。

兩個虛擬樹進行對比:
patch(oldVnode, vnode) -> patchVnode(oldVnode, vnode) -> updataChildren(oldCh, newCh)
在updataChildren(oldCh, newCh)的過程中也會進行 patchVnode(oldVnode, vnode) ,如此虛擬樹深度優先遞歸diff完成。

更加詳細直觀的圖看此鏈接
https://www.processon.com/view/5e809004e4b08e4e2447d02e

閱讀全文

與diff演算法原理是什麼相關的資料

熱點內容
dvd光碟存儲漢子演算法 瀏覽:757
蘋果郵件無法連接伺服器地址 瀏覽:962
phpffmpeg轉碼 瀏覽:671
長沙好玩的解壓項目 瀏覽:142
專屬學情分析報告是什麼app 瀏覽:564
php工程部署 瀏覽:833
android全屏透明 瀏覽:736
阿里雲伺服器已開通怎麼辦 瀏覽:803
光遇為什麼登錄時伺服器已滿 瀏覽:302
PDF分析 瀏覽:484
h3c光纖全工半全工設置命令 瀏覽:143
公司法pdf下載 瀏覽:381
linuxmarkdown 瀏覽:350
華為手機怎麼多選文件夾 瀏覽:683
如何取消命令方塊指令 瀏覽:349
風翼app為什麼進不去了 瀏覽:778
im4java壓縮圖片 瀏覽:362
數據查詢網站源碼 瀏覽:150
伊克塞爾文檔怎麼進行加密 瀏覽:892
app轉賬是什麼 瀏覽:163