天天看點

dom-diff簡易版實作dom-diff簡易版實作

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-diff簡易版實作dom-diff簡易版實作

浏覽器上檢視列印的日志資訊,如下:

dom-diff簡易版實作dom-diff簡易版實作

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

render

先周遊最外層

ul

type

props

兩個屬性

dom-diff簡易版實作dom-diff簡易版實作
dom-diff簡易版實作dom-diff簡易版實作

注意:

input

标簽的

value

屬性 還有所有标簽的

style

屬性

好了,接下來就是繼續周遊

children

屬性,此時

children

會有兩種情況

  1. 如果是文本 直接插入;
  2. 如果是子元素,遞歸周遊直到最終的結果是文本;
dom-diff簡易版實作dom-diff簡易版實作
dom-diff簡易版實作dom-diff簡易版實作

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

dom-diff簡易版實作dom-diff簡易版實作
dom-diff簡易版實作dom-diff簡易版實作

完成第一部分。

二、實作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-diff簡易版實作dom-diff簡易版實作

先處理

type

相同,屬性不同的情況。

dom-diff簡易版實作dom-diff簡易版實作
dom-diff簡易版實作dom-diff簡易版實作

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

// 更新檔包 存放兩個虛拟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這樣的一個更新檔對象中。

dom-diff簡易版實作dom-diff簡易版實作

更新檔包的

key

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

三、 打更新檔更新視圖

最後一步将更新檔的差異對象與現有虛拟DOM節點周遊進行一一比較與替換。

dom-diff簡易版實作dom-diff簡易版實作
dom-diff簡易版實作dom-diff簡易版實作

根據之前定義的不同更新檔對象結構依次處理

dom-diff簡易版實作dom-diff簡易版實作
dom-diff簡易版實作dom-diff簡易版實作

大功告成!

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

diff

算法的原理。

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

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

作者:猴子愛香蕉

連結:https://juejin.im/post/5bdd1e71e51d452da06cf59a

來源:掘金

著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。

繼續閱讀