更新element的children-双端对比diff算法
-
中间对比
-
案例
- a b ( c y e ) f g < -------- >
- a b ( e d c ) f g
-
情况分析
- 创建新的(d) (在老的里面不存在,新的里面存在)
- 删除老的(y) (在老的里面存在,新的里面不存在)
- 移动(c,e) (节点存在于新的和老的里面,但是位置变了)
-
针对相应的情况进行实现功能
- 老节点里面有,新节点里面无 如何进行判断呢
- 遍历新节点进行查找是否存在老节点,我们使用双端对比,减少了O(n) 里面的 n
- 直接使用 key来进行判断,时间空间复杂度属于 O(1)
- 测试案例
js// 5.1 // a b ( c d ) f g // a b ( e c ) f g // d 节点在新的里面是没有的 - 需要删除掉 // c 节点 props 也发生了变化 const prevChldren = [ h('p', { key: 'A'}, "A"), h('p', { key: 'B'}, "B"), h('p', { key: 'C', id: 'c-prev'}, "C"), h('p', { key: 'D'}, "D"), h('p', { key: 'F'}, "F"), h('p', { key: 'G'}, "G"), ] const nextChildren = [ h('p', { key: 'A'},'A'), h('p', { key: 'B'},'B'), h('p', { key: 'E'},'E'), h('p', { key: 'C', id: 'c-next'},'C'), h('p', { key: 'F'},'F'), h('p', { key: 'G'},'G') ] - 老节点里面有,新节点里面无 如何进行判断呢
-
实现过程
-
观察我们发现上面的测试案例, prevChildren 与 nextChildren ,我们先研究的点是
- 新的里面没有D,进行删除
- 新的里面 C 的 id 属性值不同,进行修改
-
功能实现
js// 新的比老的多 if (i > e1) { if (i <= e2) { const nextPos = i + 1 const anchor = nextPos < l2 ? c2[nextPos].el : null while (i <= e2) { patch(null, c2[i], container, parentComponent, anchor) i++ } } // 老的比新的多 } else if (i > e2) { while (i <= e1) { hostPatchRemove(c1[i].el) i++ } } else { // ✅ 中间对比 let s1 = i let s2 = i let toBePatched = e2 - s2 + 1 // ✅ 优化 let patched = 0 // ✅ 优化 const keyToNewIndexMap = new Map() for (let i = s2; i <= e2; i++) { // 将新节点的每个元素的 key 存入 map const nextChild = c2[i] keyToNewIndexMap.set(nextChild.key, i) } for (let i = s1; i <= e1; i++) { const prevChild = c1[i] if (patched >= toBePatched) { // ✅ 优化 hostPatchRemove(prevChild.el) continue; } // null,unfined let newIndex // ✅ 看老节点的 key 能不能和新节点匹配上 if (prevChild.key != null) { newIndex = keyToNewIndexMap.get(prevChild.key) } else { for (let j = s2; j <= e2; j++) { if (isSameVNodeType(prevChild, c2[j])) { newIndex = j break } } } if (newIndex === undefined) { // ✅ 新节点里面没有,就把老节点删除 hostPatchRemove(prevChild.el) } else { // ✅ 新节点里面有就进行对比渲染 patch(prevChild, c2[newIndex], container, parentComponent, null) patched++ } } } -