之前跟着视频教程写过一遍虚拟
dom
的生成和diff
算法, vue 的虚拟dom 来自于snabbdom
这个库, github代码仓库, 现在重新回顾一遍
diff 算法是干啥的
首先要知道 diff
算法到底是用来干什么的? diff
算法不是在两个集合里寻找不同的元素的, 而是找到符合你判断逻辑的相同虚拟节点。比如:vue
里面的 sameNode,下面有代码 ;找到相同虚拟节点之后,执行 patch
逻辑, 这里的patch
是 vue 里面的dom
操作 , 这里的sameNode
匹配只是判断最外层vnode
是否相同, 内部还需要 patch
函数进行操作
diff 算法执行的逻辑
匹配逻辑, 这里的匹配逻辑使用了多个指针一起走, 每一次循环都会执行一次下面的逻辑, 一次循环走过以后, 对应的下标也会改变, 这里注意下标的改变跟命中逻辑有关
// diff 算法是用来寻找相同元素进行patch的
// 下面的方法没有处理边界,
// 比如你的新标签要比旧的多, 那就需要 diff算法结束后进行一次处理, 把剩余的新标签元素插入到dom中去,
// 你的新标签如果比旧的少, 那就可能需要你删掉相应的标签
const sameNode = (newNode, node) => {
return node.key == newNode.key && node.tag === newNode.tag
}
const map = new Map()
const arr = [
{ key: 1, tag: 'div' },
{ key: 2, tag: 'div' },
{ key: 5, tag: 'div' },
{ key: 4, tag: 'div' }
]
const arr2 = [
{ key: 1, tag: 'div' },
{ key: 2, tag: 'div' },
{ key: 5, tag: 'div' },
{ key: 6, tag: 'div' }
]
let startIndex = 0
let endIndex = arr.length - 1
let nStartIndex = 0
let nEndIndex = arr2.length - 1
//命中才能改变当前下标,
while (startIndex <= endIndex && nStartIndex <= nEndIndex) {
const item = arr[startIndex]
const endItem = arr[endIndex]
const nItem = arr2[nStartIndex]
const nEndItem = arr2[nEndIndex]
// 新前和旧前的值相等
if (sameNode(nItem, item)) {
startIndex++
nStartIndex++
console.log('命中了=>现在是新前===旧前', nItem, item)
} else if (sameNode(nEndItem, endItem)) {
nEndIndex--
endIndex--
console.log('命中了=>现在是新后===旧后', nEndItem, endItem)
} else if (sameNode(nEndItem, item)) {
startIndex++
nEndIndex--
console.log('命中了=>现在是新后===旧前', nEndItem, item)
} else if (sameNode(nItem, endItem)) {
endIndex--
nStartIndex++
console.log('命中了=>现在是新前===旧后', nItem, endItem)
} else {
console.log('sdfsdfsdfsdf')
if (map.length === 0) {
arr.forEach((item, index) => {
map.set(item.key, index)
})
}
// 匹配有没有命中的元素
const index = map.get(arr2[nStartIndex].key)
if (index) {
console.log('最后一个循环找到了,执行patch')
} else {
console.log(
'直接把旧标签用新的替换掉',
`${arr2[nStartIndex].key}${arr2[nStartIndex].tag}`
)
}
nStartIndex++
}
}
解释:
1. sameNode
const sameNode = (vnode, newVnode)=>{
return vnode.key == newVnode.key && vnode.tag === newVnode.tag
}