export default function updateChildren(parentElm, oldCh, newCh) {
// 旧前
let oldStartIdx = 0;
// 新前
let newStartIdx = 0;
// 旧后
let oldEndIdx = oldCh.length - 1;
// 新后
let newEndIdx = newCh.length - 1;
// 旧前节点
let oldStartVnode = oldCh[0];
// 旧后节点
let oldEndVnode = oldCh[oldEndIdx];
// 新前节点
let newStartVnode = newCh[0];
// 新后节点
let newEndVnode = newCh[newEndIdx];
let keyMap = null;
while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
// 略过已经加undefined标记的内容
if (oldStartVnode == null || oldCh[oldStartIdx] === undefined) {
oldStartVnode = oldCh[++oldStartIdx];
}
else if (oldEndVnode == null || oldCh[oldEndIdx] === undefined) {
oldEndVnode = oldCh[--oldEndIdx];
}
else if (newStartVnode == null || newCh[newStartIdx] === undefined) {
newStartVnode = newCh[++newStartIdx];
}
else if (newEndVnode == null || newCh[newEndIdx] === undefined) {
newEndVnode = newCh[--newEndIdx];
}
else if (checkSameVnode(oldStartVnode, newStartVnode)) {
// 新前与旧前
console.log('新前与旧前命中');
patchVnode(oldStartVnode, newStartVnode);
oldStartVnode = oldCh[++oldStartIdx];
newStartVnode = newCh[++newStartIdx];
}
else if (checkSameVnode(oldEndVnode, newEndVnode)) {
// 新后和旧后
console.log('新后和旧后命中');
patchVnode(oldEndVnode, newEndVnode);
oldEndVnode = oldCh[--oldEndIdx];
newEndVnode = newCh[--newEndVnode];
}
else if (checkSameVnode(oldStartVnode, newEndVnode)) {
console.log('新后和旧前命中');
patchVnode(oldStartVnode, newEndVnode);
// 当新后与旧前命中的时候,此时要移动节点,移动新后指向的这个节点到老节点旧后的后面
parentElm.insertBefore(oldStartVnode.elm, oldEndVnode.elm.nextSibling);
oldStartVnode = oldCh[++oldStartIdx];
newEndVnode = newCh[--newEndIdx];
}
else if (checkSameVnode(oldEndVnode, newStartVnode)) {
// 新前和旧后
console.log('新前和旧后命中');
patchVnode(oldEndVnode, newStartVnode);
// 当新前和旧后命中的时候,此时要移动节点,移动新前指向的这个节点到老节点旧前的前面
parentElm.insertBefore(oldEndVnode.elm, oldStartVnode.elm);
oldEndVnode = oldCh[--oldEndIdx];
newStartVnode = newCh[++newStartIdx];
}
else {
// 四种都没有命中
// 制作keyMap一个映射对象,这样就不用每次都遍历老对象了
if (!keyMap) {
keyMap = {};
for (let i = oldStartIdx; i <= oldEndIdx; i++) {
const key = oldCh[i].key;
if (key !== undefined) {
keyMap[key] = i;
}
}
}
// 寻找当前这项(newStartIdx)在keyMap中的映射的位置序号
const idxInOld = keyMap[newStartVnode.key];
if (idxInOld === undefined) {
// 如果idxInOld是undefined表示踏实全新的项,此时会将该项创建为DOM节点并插入到旧前之前
parentElm.insertBefore(createElement(newStartVnode), oldStartVnode.elm);
}
else {
// 如果不是undefined,则不是全新的项,则需要移动
const elmToMove = oldCh[idxInOld];
patchVnode(elmToMove, newStartVnode);
// 把这项设置为undefined,表示已经处理完这项了
oldCh[idxInOld] = undefined;
// 移动
parentElm.insertBefore(elmToMove.elm, oldStartVnode.elm);
}
// 指针下移,只移动新的头
newStartVnode = newCh[++newStartIdx];
}
}
// 循环结束后,处理未处理的项
if (newStartIdx <= newEndIdx) {
console.log('new还有剩余节点没有处理,要加项,把所有剩余的节点插入到oldStartIdx之前');
// 遍历新的newCh,添加到老的没有处理的之前
for (let i = newStartIdx; i <= newEndIdx; i++) {
// insertBefore方法可以自动识别null,如果是null就会自动排到队尾去
// newCh[i]现在还没有真正的DOM,所以要调用createElement函数变为DOM
parentElm.insertBefore(createElement(newCh[i]), oldCh[oldStartIdx].elm);
}
}
else if (oldStartIdx <= oldEndIdx) {
console.log('old还有剩余节点没有处理,要删除项');
// 批量删除oldStart和oldEnd指针之间的项
for (let i = oldStartIdx; i <= oldEndIdx; i++) {
if (oldCh[i]) {
parentElm.removeChild(oldCh[i].elm);
}
}
}
}
评论