流程详解
待补充
简化版代码
const patchKeyedChildren = (
c1,
c2
) => {
let i = 0
let l1 = c1.length,
l2 = c2.length
let e1 = l1 - 1 // 旧的最后一个index
let e2 = l2 - 1 // 新的最后一个index
// 1 和 2 预处理相同的前后节点
// 1. 从头开始
while (i <= e1 && i <= e2) {
const n1 = c1[i]
const n2 = c2[i]
if (isSameVNodeType(n1, n2)) {
// 判断为相同节点
console.log('从头开始,patch: ', n1)
} else {
break
}
i++
}
// 2. 从尾部开始
while (i <= e1 && i <= e2) {
const n1 = c1[e1]
const n2 = c2[e2]
if (isSameVNodeType(n1, n2)) {
// 判断为相同节点
console.log('从尾部开始,patch: ', n1)
} else {
break
}
e1--
e2--
}
/**
* 预处理之后:
* (a, b, c, d), (g) e1 === 3
* (a, b, c, d), e, f, (g) e2 === 4
* i === 4
*/
/**
* 3. 新增节点
* 如果i > e1 && i <= e2,说明新arr有新增的节点
*/
if (i > e1) {
if (i <= e2) {
while (i <= e2) {
console.log('新增的节点patch: ' + c2[i])
i++
}
}
}
// 4. 减少节点 --- 直接删除旧的节点
else if (i > e2) {
while (i <= e1) {
console.log('删除旧节点:', c1[i])
i++
}
}
// 5. 有节点移动、新增或删除
else {
const s1 = i // 旧的初始index
const s2 = i // 新的初始index
// 5.1 建立新序列的Map
const keyToNewIndexMap = new Map()
for (let idx = s2; idx <= e2; idx++) {
const nextChild = c2[idx]
if (nextChild.key !== null) {
keyToNewIndexMap.set(nextChild.key, idx)
}
}
console.log(keyToNewIndexMap)
// 5.2 循环旧序列
let j
const toBePatched = e2 - s2 + 1 // 总的需要patch的数量
let patched = 0 // 已patch的数量
let moved = false // 移动标记
let maxNewIndexSoFar = 0 // 已遍历的待处理的 c1 节点在 c2 中对应的索引最大值
const newIndexToOldIndexMap = new Array(toBePatched) // 代表新节点在旧序列中的位置,用于后面求最长递增子序列
for (i = 0; i < toBePatched; i++) newIndexToOldIndexMap[i] = 0 // 默认全部为新增节点,用0表示
// 遍历 c1 中待处理的节点,判断否 c2 中是有相同 key 的节点存在。
for (i = s1; i <= e1; i++) {
const prevChild = c1[i]
if (patched >= toBePatched) {
// 已patch的数量比未patch的多,说明多余,删掉节点
console.log('已patch的数量比未patch的多,说明多余,删掉节点')
continue
}
// 否则,调用 patch 函数,并记录节点在 c1 中的索引。
// 同时,记录节点在 c2 中的最大索引
let newIndex
newIndex = keyToNewIndexMap.get(prevChild.key) // 取旧节点在新序列里的索引
console.log(`旧节点${prevChild.key}, 在新序列里面对应索引的:${ newIndex !== undefined ? newIndex : '找不到' }`)
if (newIndex === undefined) {
// 在旧的里面找不到了,说明没用,删掉
console.log(prevChild, '在新序列中没有了,删除节点')
} else {
// 把0当成特殊值(代表新增的节点),所以其他位置用i + 1代替
newIndexToOldIndexMap[newIndex - s2] = i + 1
if (newIndex >= maxNewIndexSoFar) {
maxNewIndexSoFar = newIndex
} else {
// 假如节点在 c2 中的索引位置小于这个最大索引,那么说明是有元素需要进行移动。
moved = true
}
console.log('patch - 复用节点:', prevChild)
patched++
}
}
console.log('新节点在旧序列中的位置:', newIndexToOldIndexMap)
// 5.3
for (i = toBePatched - 1; i >= 0; i--) {
if (newIndexToOldIndexMap[i] === 0) {
console.log('新创建:', c2[i + s2])
} else if (moved) {
console.log('移动旧节点:', c2[i + s2])
}
}
}
}