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); } } }}
评论