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插入到頁面中,那我們可以專門寫一個渲染真實節點的方法
render
先周遊最外層
ul
的
type
和
props
兩個屬性
注意:
input
标簽的
value
屬性 還有所有标簽的
style
屬性
好了,接下來就是繼續周遊
children
屬性,此時
children
會有兩種情況
- 如果是文本 直接插入;
- 如果是子元素,遞歸周遊直到最終的結果是文本;
下一步我們将這個實際的DOM元素結構插入到頁面中
完成第一部分。
二、實作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結構如下:
先處理
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
作者:猴子愛香蕉
連結:https://juejin.im/post/5bdd1e71e51d452da06cf59a
來源:掘金
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。