我們知道virtual dom的作用是為了避免直接操作dom,因為直接操作dom是一件很費效能的事情,但是虛擬dom最後要渲染成真實的dom,也是需要採用dom操作的,那效能優化,優化在**?
var element = ,
children: [ // 該節點的子節點
, children: ["item 1"]},
, children: ["item 2"]},
, children: ["item 3"]},
]}
上面對應的html結構是:
既然dom節點可以用js物件來表示,那麼一整個dom樹,當然也可以用js物件來表示。
js物件模擬dom物件的乙個簡單的實現:
export default class element tag 'div'
* @param props
* @param children [ element1, 'text']
* @param key option
*/constructor(tag, props, children, key) else if (isstring(children))
if (key) this.key = key
} // 渲染
render()
create()
// 建立節點
_createelement(tag, props, child, key)
}if (key)
// 遞迴新增子節點
if (child) else })}
return el
}}
我們在比對dom樹差異的時候,是會用到樹的遞迴的,在我們實現之前,先考慮好兩個節點之間的對比會有哪些情況
import from './util'
import element from './element'
export default function diff(olddomtree, newdomtree)
// 一開始的索引為 0
dfs(olddomtree, newdomtree, 0, pathchs)
return pathchs
}function dfs(oldnode, newnode, index, patches) else if (newnode.tag === oldnode.tag && newnode.key === oldnode.key) )
// 遍歷子樹
diffchildren(oldnode.children, newnode.children, index, patches)
} else )
} if (curpatches.length) else
}}
判斷屬性更改也分為三個步驟
function diffprops(oldprops, newprops) )
}} for (const key in newprops) )
} else if (!oldprops[key]) )}}
} return change
}
這裡的步驟其實跟判斷屬性差異類似的,也是分為三步。
function listdiff(oldlist, newlist, index, patches)
// 尋找新的 children 中是否含有當前節點
// 沒有的話需要刪除
let index = newkeys.indexof(key)
if (index === -1) else list.push(key)
})// 遍歷變更後的陣列
let length = list.length
// 因為刪除陣列元素是會更改索引的
// 所有從後往前刪可以保證索引不變
for (let i = length - 1; i >= 0; i--) )
}} // 遍歷新的 list,判斷是否有節點新增或移動
// 同時也對 `list` 做節點新增和移動節點的操作
newlist &&
newlist.foreach((item, i) =>
// 尋找舊的 children 中是否含有當前節點
let index = list.indexof(key)
// 沒找到代表新節點,需要插入
if (index === -1 || key == null) )
list.splice(i, 0, key)
} else )
move(list, index, i)}}
})return
}function getkeys(list) else if (item instanceof element)
keys.push(key)
})return keys
}
對於這個函式來說,主要功能就兩個
function diffchildren(oldchild, newchild, index, patches) = listdiff(oldchild, newchild, index, patches)
if (changes.length) else
} // 記錄上乙個遍歷過的節點
let last = null
oldchild &&
oldchild.foreach((item, i) =>
} else index += 1
last = item
})}
通過之前的演算法,我們已經可以得出兩個樹的差異了。既然知道了差異,就需要區域性去更新 dom 了,下面就讓我們來看看 virtual dom 演算法的最後一步驟
這個函式的主要兩個功能
let index = 0
export default function patch(node, patchs)
let last = null
if (childnodes && childnodes.length) )
}}function changedom(node, changes, nochild) = change
switch (type) = change
props.foreach(item => else
})break
case stateenums.remove:
node.childnodes[change.index].remove()
break
case stateenums.insert:
let dom
if (isstring(change.node)) else if (change.node instanceof element)
node.insertbefore(dom, node.childnodes[change.index])
break
case stateenums.replace:
node.parentnode.replacechild(change.node.create(), node)
break
case stateenums.move:
let fromnode = node.childnodes[change.from]
let tonode = node.childnodes[change.to]
let clonefromnode = fromnode.clonenode(true)
let cloentonode = tonode.clonenode(true)
node.replacechild(clonefromnode, tonode)
node.replacechild(cloentonode, fromnode)
break
default:
break}})
}
virtual dom演算法的實現主要總結為三步
let test4 = new element('div', , ['test4'])
let test5 = new element('ul', , ['test5'])
let test1 = new element('div', , [test4])
let test2 = new element('div', , [test5, test4])
let root = test1.render()
let pathchs = diff(test1, test2)
console.log(pathchs)
settimeout(() => , 1000)
優缺點:
優點:
缺點:
虛擬dom 虛擬 DOM 和 DOM diff
乙個能代表dom樹的物件,通常含有標籤名 標籤上的屬性 事件監聽和子元素及其它屬性。虛擬dom在vue和react中的示例 const rnode classname red 標籤上的屬性 onclick 事件 ref null,type div 標籤名 or 元件名 const vnode 事件 ...
vue虛擬DOM是什麼?vue的虛擬DOM的用法
vue虛擬dom是什麼?vue的虛擬dom的用法 1 為什麼需要虛擬dom 雖然採用的是文件碎片,但是操作的還是真實的dom。而我們知道操作dom的代價是昂貴的,所以vue2.0採用了虛擬dom來代替對真實dom的操作,最後通過某種機制來完成對真實dom的更新,渲染檢視。所謂的虛擬dom,其實就是用...
虛擬DOM與真實DOM
先來個簡單的react例子 1.建立虛擬dom const vdom hello react h1 此處不要寫引號,因為不是字串 2.渲染虛擬dom到頁面 reactdom.render vdom document.getelementbyid test script head test div b...