react/vue中dom-diff簡易版實現

猴子愛香蕉發表於2019-03-01

dom-diff簡易版實現

一、建立虛擬dom

利用 create-react-app快速建立一個專案模板;

刪掉src下的原始檔,替換成 index.js

首先我們先要用一個物件定義一個虛擬DOM的資料結構:

Element {
    type: 'ul',
    props: {
        class: 'list'
    },
    children: [
        Element{
            type: 'li',
            props: {
                class: 'item'
            },
            children: ['a']
        }
    ]
}
複製程式碼

開始碼程式碼實現虛擬dom的方法實現。

'虛擬DOM結構'
瀏覽器上檢視列印的日誌資訊,如下:

控制檯日誌

既然虛擬DOM方法已經寫好,下一步就要將這個虛擬dom插入到頁面中,那我們可以專門寫一個渲染真實節點的方法render

先遍歷最外層ultypeprops兩個屬性

render

控制檯日誌

注意:input標籤的value屬性 還有所有標籤的style屬性

好了,接下來就是繼續遍歷children屬性,此時children會有兩種情況

  1. 如果是文字 直接插入;
  2. 如果是子元素,遞迴遍歷直到最終的結果是文字;

遍歷虛擬don元素轉換為真實dom結構

控制檯日誌

下一步我們將這個實際的DOM元素結構插入到頁面中

append
控制檯日誌

完成第一部分。


二、實現dom-diff演算法

dom-diff演算法就是在兩棵抽象語法樹的同一位置採用先序的深度遍歷演算法做比較,同時用補丁的形式記錄需要更新的節點位置。

type不一致直接替換當前節點以及當前節點下的子節點; 如果兩個父節點一致,則從左往後遍歷子節點,若子節點一致,遍歷子節點下的子節點,依次遞迴。

補丁包的定義規則如下:

1. 屬性不同(type: 'ATTRS', attrs)
2. 新的節點被刪除了 (type: 'REMOVE', index: xxxx)
3. 節點型別不同/新增 (type: 'REPLACE', newNode)
4. 僅僅是文字變化(type: 'TEXT', text)
複製程式碼

新建一個dom-diff.js,專門處理diff演算法

手動呼叫diff方法(react中呼叫diff演算法是在觸發setState之後)

兩個虛擬dom結構如下:

虛擬dom結構

先處理type相同,屬性不同的情況。

屬性不同

控制檯日誌

發現控制檯已經列印到屬性變化的補丁包,最後我們把屬性的小補丁包存放到最外層的大補丁包中

// 補丁包 存放兩個虛擬dom的差異部分
let patchs = {}
// 放到最外層的大補丁包中
if (currentPatchs.length > 0) {
  patchs[index] = currentPatchs
}
複製程式碼

好了 相同型別的父節點一樣,在屬性比較完成之後,就需要比較children的屬性是否有變化 比較children屬性內部元素是否變化,利用遞迴去遍歷

let globalIndex = 0

function diffChildren (oldChildrens, newChildrens) {
  oldChildrens.forEach((child, idx) => {
    walk(child, newChildrens[idx], ++globalIndex)
  })
}
複製程式碼

如果一開始type型別不相同不需要再去比較,直接用新節點替換老節點即可;

// type不一致
currentPatchs.push({
  type: TYPES.REPLACE,
  newNode: newTree
})
複製程式碼

相容並處理好各種情況,比如:新節點不存在的情況,新節點增加,新節點型別改變,新節點文字改變以及新節點的屬性變化等情況;

最終拿到所有與舊節點有差異的物件放入patchs這樣的一個補丁物件中。

控制檯日誌

補丁包的key就是對應新節點有變化的資料位置。


三、 打補丁更新檢視

最後一步將補丁的差異物件與現有虛擬DOM節點遍歷進行一一比較與替換。

開始打補丁

補丁步驟

根據之前定義的不同補丁物件結構依次處理

補丁步驟

控制檯日誌

大功告成!


這只是diff演算法的一個簡易實現,還存在一些複雜情況處理的情況以及還有很多演算法上面優化的方案,不過已經讓我們大概瞭解了diff演算法的原理。

如有筆誤或者其他實現不對的地方,還望大家指出,謝謝!

具體程式碼可以參考github連結檢視:dom-diff-demo

相關文章