- 预处理
- j从新旧子节点头部开始处理相同key节点
- 两指针从两子节点尾开始处理相同节点
- 新节点如果已处理完,旧节点没有处理完,卸载旧节点
- 旧节点已处理完,新节点没有处理完,逐个插入新节点
新旧节点都没处理完
- 找到新节点对应的旧节点索引,如[2,3,1,-1]
- -1代表新节点需要插入
- 计算最长递增子序列
- 从新节点尾部往前遍历
如果不在最长子序列中,说明需要将当前节点的DOM往后移、
function patchKedChildren3(n1, n2, container) {
const oldChildren = n1.children
const newChildren = n2.children
//预处理
let j = 0
let oldVNode = oldChildren[j]
let newVNode = newChildren[j]
while (newVNode.key === oldVNode.key) {
patch(oldVNode, newVNode, container)
j++
oldVNode = oldChildren[j]
newVNode = newChildren[j]
}
let oldEndIndex = oldChildren.length - 1
let newEndIndex = newChildren.length - 1
oldVNode = oldChildren[oldEndIndex]
newVNode = newChildren[newEndIndex]
while (newVNode.key === oldVNode.key) {
patch(oldVNode, newVNode, container)
oldEndIndex--
newEndIndex--
oldVNode = oldChildren[oldEndIndex]
newVNode = newChildren[newEndIndex]
}
//如果旧子节点已处理结束,新子节点没有,则新增
if (oldEndIndex < j && newEndIndex >= j) {
const anchorIndex = newEndIndex + 1
const anchor =
anchorIndex < newChildren.length ? newChildren[anchorIndex].el : null
while (j <= newEndIndex) {
patch(null, newChildren[j], container, anchor)
j++
}
//如果新子节点已处理结束,旧子节点没有,则卸载全部未处理旧子节点
} else if (newEndIndex < j && oldEndIndex >= j) {
while (j <= oldEndIndex) {
unmount(oldChildren[j++])
}
//处理交换位置
} else {
const count = newEndIndex - j + 1
const source = new Array(count)
source.fill(-1)
const indexMap = {}
const pos = 0
const newStart = j
const oldStart = j
//indexMap存储形式{key,index}
for (let i = newStart; i <= newEndIndex; i++) {
indexMap[newChildren[i].key] = i
}
let patched = 0
let move = false
for (let i = oldStart; i <= oldEndIndex; i++) {
const oldVNode = oldChildren[i]
if (patched > count) {
const k = indexMap[oldVNode.key]
if (k) {
const newVNode = newChildren[ind]
patched++
patch(oldVNode, newVNode, container)
source[k - newStart] = i
if (k < pos) {
move = true
} else {
pos = k
}
} else {
unmount(oldVNode)
}
} else {
unmount(oldVNode)
}
}
//递增子序列,不需移动
const seq = getSequence(source)
//s指向递增子序列最后
let s = seq.length - 1
//i指向需要移动的子节点最后
let i = count - 1
for (i; i >= 0; i--) {
//等于-1说明是新增需要插入
if (source[i] === -1) {
//children中的位置
const pos = i + newStart
const anchorIndex = pos + 1
const anchor = newChildren[anchorIndex]
? newChildren[anchorIndex].el
: null
patch(null, newChildren[pos], container, anchor)
//i比当前seq指针位置小,说明i处的新子节点需要移到最后
} else if (i !== seq[s]) {
if (i < seq[s]) {
const pos = i + newStart
const anchorIndex = pos + 1
const anchor = newChildren[anchorIndex]
? newChildren[anchorIndex].el
: null
insert(newChildren[pos].el, container, anchor)
}
//节点一样s上移一位
} else {
s--
}
}
}
}