34-mini-vue 更新element的children-双端对比diff算法

更新element的children,两端diff算法3

  1. 中间对比 顺序调整
  • 暴力排序 新节点里面的元素在老节点里面存在不存在,存在的话就依次插入到新的位置
  • 找到递增序列(下面案例中 c d 是递增序列) 剩下的元素进行移动
    • 判断是否是递增序列,不是的话就进行移动
    • 上面的方案可行,我们找到最长递增子序列,剩下的元素进行移动,让不需要移动的元素尽可能的保持不动
  1. 案例
  • a b ( c d e ) f g < -------- >
  • a b ( e c d ) f g
  1. 情况分析
  • 初始化中间对比时的数组的值
js 复制代码
    const newIndexToOldIndexMap = new Array(toBePatched) // ✅ 初始化中间需要调整位置的数组下标,值都统一设置为0
    for (let i = 0; i < toBePatched; i++) newIndexToOldIndexMap[i] = 0 // ✅

    if (newIndex === undefined) { // 新节点里面没有,就把老节点删除
        hostPatchRemove(prevChild.el)
    } else { // 新节点里面有就进行对比渲染
        newIndexToOldIndexMap[newIndex - s2] = i + 1 // ✅ 初始化数组的下标
        patch(prevChild, c2[newIndex], container, parentComponent, null)
        patched++
    }
  • 我们直接复制最长递增子序列的函数到 renderer.ts 文件,直接求出想要的最长递增子序列的下标
js 复制代码
  function getSequence(arr) {
    const p = arr.slice();
    const result = [0];
    let i, j, u, v, c;
    const len = arr.length;
      for (i = 0; i < len; i++) {
          const arrI = arr[i];
          if (arrI !== 0) {
              j = result[result.length - 1];
              if (arr[j] < arrI) {
                  p[i] = j;
                  result.push(i);
                  continue;
              }
              u = 0;
              v = result.length - 1;
              while (u < v) {
                  c = (u + v) >> 1;
                  if (arr[result[c] as any] < arrI) {
                      u = c + 1;
                  } else {
                      v = c;
                  }
              }
              if (arrI < arr[result[u] as any]) {
                  if (u > 0) {
                      p[i] = result[u - 1];
                  }
                  result[u] = i;
              }
          }
      }
      u = result.length;
      v = result[u - 1];
      while (u-- > 0) {
          result[u] = v;
          v = p[v];
      }
      return result;
  }
js 复制代码
    // 生成最长子序列
    const increasingNewIndexSequence = getSequence(newIndexToOldIndexMap)
    // 创建一个j 作为最长递增子序列的指针
    let j = 0
    for(let i = 0; i < toBePatched; i++){
      if(i !== increasingNewIndexSequence[j]) {
        console.log('移动位置');
      } else {
        j++
      }
    }
  • 移动位置如何进行移动呢?

    • 思路:我们可以设置 insert 方法

    • 比如新数组为 a b e c d f g, e c d 属于中间乱序部分

    • 我们 e 要插在 c 的前面,但 c 的位置还没有固定,且如果 c 也属于不稳定元素,那不就不能确定 e 的位置了?

    • 所以我们需要倒序处理中间的元素,当我们处理 d 时,哎?f属于中间乱序之外的元素,且可以确定位置,f搞定以后处理 d 时,我们就可以把 f 当做锚点

    • 因此我们需要更改逻辑

      js 复制代码
      // 生成最长子序列
      const increasingNewIndexSequence = getSequence(newIndexToOldIndexMap)
      // 创建一个j 作为最长递增子序列的指针
      let j = toBePatched - 1 // ✅ 
      for(let i = toBePatched - 1; i >= 0; i--){ // ✅
      // 当前要处理的节点的下一个就是锚点
      const nextIndex = i + s2
      const nextChild = c2[nextIndex]
      const anchor = nextIndex + 1 < l2 ? c2[ nextIndex + 1 ].el : null
      
      if(i !== increasingNewIndexSequence[j]) {
        console.log('移动位置');
        hostInsert(nextChild.el, container, anchor) // 根据锚点移动位置
      } else {
        j-- // ✅
      }
      }
  • 上面已经基本完成了中间乱序的排序,下面是一些优化的点

    • j 在 < 0 时,说明不需要移动的子序列已全部处理完毕,此时符合条件,直接可以进行移动

    • 最长递增子序列的算法,可以提前进行判断是否需要,如果不需要,直接给一个空数组更加快捷,如何进行判断呢?

      • 中间的乱序数组,由旧元素,映射成为新元素,如果旧元素,到新元素,下标是一个变大的效果,如果一个元素下标小于其他元素,那么它其实就能确认是移动了位置的。不移动的话下标是递增的效果。
      • 根据上面这个可以记录哪些元素进行了移动
      js 复制代码
      let moved = false // ✅ 记录是否需要进行移动
      let maxNewIndexSoFar = 0 // ✅ 如果小于该值就需要移动
      
      if (newIndex === undefined) {
          hostPatchRemove(prevChild.el)
        } else {
          // ✅ 设置锁的时机
          if(newIndex >= maxNewIndexSoFar) {
            maxNewIndexSoFar = newIndex 
          } else {
            moved = true
          }
          newIndexToOldIndexMap[newIndex - s2] = i + 1
          patch(prevChild, c2[newIndex], container, parentComponent, null)
          patched++
        }
      
      // ✅
      const increasingNewIndexSequence = moved ? getSequence(newIndexToOldIndexMap) : []
      
      let j = toBePatched - 1 
        for(let i = toBePatched - 1; i >= 0; i--){
        
        const nextIndex = i + s2
        const nextChild = c2[nextIndex]
        const anchor = nextIndex + 1 < l2 ? c2[ nextIndex + 1 ].el : null
        if(moved) { // ✅
          if(j < 0 && i !== increasingNewIndexSequence[j]) { // j < 0 说明需要不需要移动的子序列已全部处理完毕
            console.log('移动位置');
            hostInsert(nextChild.el, container, anchor)
          } else {
            j--
          }
        }
      }
  • 创建新节点,其实在上面处理移动的过程中,我们已经找到了创建的时机

js 复制代码
const newIndexToOldIndexMap = new Array(toBePatched) // 初始化中间需要调整位置的数组下标,值都统一设置为0
for(let i = 0; i < toBePatched; i++) newIndexToOldIndexMap[i] = 0

if (newIndex === undefined) { // 新节点里面没有,就把老节点删除
  hostPatchRemove(prevChild.el)
} else { // 新节点里面有就进行对比渲染
  if(newIndex >= maxNewIndexSoFar) {
    maxNewIndexSoFar = newIndex 
  } else {
    moved = true
  }
  newIndexToOldIndexMap[newIndex - s2] = i + 1 // 初始化数组的下标
  patch(prevChild, c2[newIndex], container, parentComponent, null)
  patched++
}
// 生成最长子序列
const increasingNewIndexSequence = moved ? getSequence(newIndexToOldIndexMap) : []
// 创建一个j 作为最长递增子序列的指针
let j = toBePatched - 1 
for(let i = toBePatched - 1; i >= 0; i--){
  // 当前要处理的节点的下一个就是锚点
  const nextIndex = i + s2
  const nextChild = c2[nextIndex]
  const anchor = nextIndex + 1 < l2 ? c2[ nextIndex + 1 ].el : null
  if (newIndexToOldIndexMap[i]===0) {  // ✅ 
    patch(null, nextChild, container, parentComponent, anchor)
  }else if(moved) {
    if(j < 0 ||S i !== increasingNewIndexSequence[j]) { // j < 0 说明需要不需要移动的子序列已全部处理完毕
      console.log('移动位置');
      hostInsert(nextChild.el, container, anchor)
    } else {
      j--
    }
  }
}
相关推荐
小二·2 小时前
Python Web 开发进阶实战:数字孪生平台 —— 在 Flask + Vue 中构建实时物理世界镜像
前端·vue.js·python
Qhumaing2 小时前
数据结构——例子求算法时间复杂度&&空间复杂度
数据结构·算法
ashcn20012 小时前
websocket测试通信
前端·javascript·websocket
鱼跃鹰飞2 小时前
Leetcode1027:最长等差数列
java·数据结构·算法
翱翔的苍鹰2 小时前
CIFAR-10 是一个经典的小型彩色图像分类数据集,广泛用于深度学习入门、模型验证和算法研究
深度学习·算法·分类
顶点多余2 小时前
静态链接 vs 动态链接,静态库 vs 动态库
linux·c++·算法
吃吃喝喝小朋友2 小时前
JavaScript文件的操作方法
开发语言·javascript·ecmascript
Trae1ounG2 小时前
模块间通信解耦
javascript
2501_944526422 小时前
Flutter for OpenHarmony 万能游戏库App实战 - 知识问答游戏实现
android·开发语言·javascript·python·flutter·游戏·harmonyos