- 简化了 Vue2 diff 的过程,主要专注于子节点之间的 diff,下面是一个手写的 diff 算法,跟源码有细微的差别,主要为了理解。
const insertBefore = (ele, refer, nodes) => {
const target = nodes.findIndex(e => e === ele)
if (~target) {
nodes.splice(target, 1)
}
nodes.splice(nodes.findIndex(e => e === refer), 0, ele)
}
const insertAfter = (ele, refer, nodes) => {
const target = nodes.findIndex(e => e === ele)
if (~target) {
nodes.splice(target, 1)
}
nodes.splice(nodes.findIndex(e => e === refer) + 1, 0, ele)
}
// const makeIndexMap = (nodes, startIndex, endIndex) => {
// let ret = {}
// for (let i = startIndex
// ret[nodes[i]] = i
// }
// return ret
// }
const oldNodes = ['e', 'd', 'g', 'j', 'b', 'c', 'a']
const newNodes = ['g', 'c']
// 四个指针,分别指向新老节点的头尾
function vue2Diff(oldNodes, newNodes) {
let oldStartIndex = 0, newStartIndex = 0
// let oldNodesIndex
let [oldEndIndex, newEndIndex] = [oldNodes.length - 1, newNodes.length - 1]
// 主要是先通过新节点去对比旧节点,从而去操作老节点的移动。
// 假如其中有一方已经遍历完了,那么就是表明可单个元素的 diff 已经完成,接下来就是进入批量删除或者批量添加的过程
while (oldStartIndex <= oldEndIndex && newStartIndex <= newEndIndex) {
let [oldStartNode, newStartNode] = [oldNodes[oldStartIndex], newNodes[newStartIndex]]
let [oldEndNode, newEndNode] = [oldNodes[oldEndIndex], newNodes[newEndIndex]]
// 假如新老节点的首位是相同的,那么新老指针同时向后移动
if (oldStartNode === newStartNode) {
oldStartIndex++
newStartIndex++
}
// 假如新老节点的末位是相同的,那么新老指针同时向前移动
else if (oldEndNode === newEndNode) {
oldEndIndex--
newEndIndex--
}
// 假如老节点的首位等于新节点的末尾,那么将老节点的首位,移动到 oldEndIndex 后面
// 同时老节点的指针前移,新节点的指针后移
else if (oldStartNode === newEndNode) {
insertAfter(oldStartNode, oldEndNode, oldNodes)
oldEndIndex--
newStartIndex++
}
// 假如老节点的末位等于新节点的首位,那么将老节点的末位结束移动到 oldStartIndex 前面
// 同时将新老节点的指针一起前移
else if (oldEndNode === newStartNode) {
insertBefore(oldEndNode, oldStartNode, oldNodes)
oldStartIndex++
newStartIndex++
} else {
// 源代码里面是将新节点的 key 对应 下标生成一个对象,方便新节点找到其在老节点中的下标
// if (!oldNodesIndex) oldNodesIndex = makeIndexMap(oldNodes, oldStartIndex, oldEndIndex)
// 这里简化了操作,直接获取其在老节点中的下标
const newInOldIndex = oldNodes.indexOf(newStartNode)
// 假如新节点在老节点里面,那么就将找到的老的节点移动到 newStartIndex 的前面,为什么要放在 newStartIndex 前面
// 同时新老指针前移
if (~newInOldIndex) {
insertBefore(oldNodes[newInOldIndex], oldStartNode, oldNodes)
oldStartIndex++
newStartIndex++
//
}
// 假如新节点不在老节点里面,那么就将找到的老的节点移动到 newStartIndex 的前面,为什么要放在 newStartIndex 前面
// 同时新老指针前移
else {
insertBefore(newStartNode, oldStartNode, oldNodes)
oldStartIndex++
oldEndIndex ++
newStartIndex++
}
}
}
// 假如到最后还有多余的元素,那么就批量添加或批量删除
if (oldEndIndex - oldStartIndex >= 0) {
oldNodes.splice(oldStartIndex, oldEndIndex - oldStartIndex + 1)
} else {
oldNodes.splice(oldStartIndex, 0, ...newNodes.slice(newStartIndex, newEndIndex + 1))
}
}
vue2Diff(oldNodes, newNodes)