第十一章-快速diff算法

72 阅读3分钟

快速diff

1、去掉相同的前置元素 j

2、去掉相同的后置元素 newEnd oldEnd

3、如果 j <= newEnd && j > oldEnd 需要挂载元素 j 到newEnd的元素, 挂载点为 newChildren[newEnd + 1]

4、如果 j <= oldEnd && j > newEnd 需要删除元素 j 到oldEnd的元素

5、如果 j <= newEnd && j <= oldEnd

​ 5.1、建立数组source, 用于存放新vnode在旧vnode中位置

​ 5.2、遍历新的vnode数组, 从j 到 newEnd 建立映射表 keyIndex = { key值 : 序号值}

​ 5.3、遍历旧的vnode数组, 通过keyIndex确定旧节点是否存在新结点中, 并且回填数组source

  • 不存在则卸载节点

  • 如果存在

    • 统计已经更新的节点的数量patched, 如果patched大于新节点数量newEnd - j + 1, 剩余节点直接卸载

    • patch更新节点

    • **用pos记录旧节点在新节点的位置的最大值, 如果没有值小于pos, 则说明没有移动, 不需要移动,更新pos, 如有值大于pos, 则设置moved为true **

    • moved为true时候, 获取最长递增子序列seq(记录newVnode最长递增的序号)

      • 倒叙遍历newVnode中没有被前面步骤1、2去掉的元素,遍历序号为i

      • 如果source[i]为 - 1, 则新建节点, 挂载点为当前节点下面一个节点,

      • 如果遍历的序号i, 在最长递增子序列seq能够找到, 继续遍历

      • 如果不能找到, 则插入节点, 挂载点为当前节点下面一个节点

注意: 为什么最后的挂载点为当前节点下面一个节点, 因为是倒叙遍历, 下面那个节点一定是已经插入, 或者当前节点就是第一个插入的节点

        const newChildren = n2.children;
        const oldChildren = n1.children;
        let j = 0;
        let oldVnode = oldChildren[j];
        let newVnode = newChildren[j]
        while (oldVnode.key === newVnode.key) {
          patch(oldVnode, newVnode, container);
          j++;
          oldVnode = oldChildren[j];
          newVnode = newChildren[j];
        }
        let oldEnd = oldChildren.length - 1;
        let newEnd = newChildren.length - 1;
        oldVnode = oldChildren[oldEnd];
        newVnode = newChildren[newEnd];
        while (oldVnode.key === newVnode.key) {
          patch(oldVnode, newVnode, container);
          oldVnode = oldChildren[--oldEnd];
          newVnode = newChildren[--newEnd];
        }
        
        if(j > oldEnd && j <= newEnd) {
          const anchorIndex = newEnd + 1;
          const anchor = anchorIndex < newChildren.length ? newChildren[anchorIndex].el : null;
          while (j <= newEnd) {
            patch(null, newChildren[j++], container, anchor)
          }
        } else if(j > newEnd && j <= oldEnd) {
          while (j <= oldEnd) {
            unmount(oldChildren[j++])
          }
        } else {
          const count = newEnd - j + 1;
          const source = new Array(count)
          source.fill(-1);
          const oldStart = j;
          const newStart = j;
          const keyIndex = {};
          let moved = false;
          let pos = 0;
          let patched =  0;
          for(let i = newStart; i <= newEnd; i++) {
            keyIndex[newChildren[i].key] = i;
          }
          for(let i = oldStart; i <= oldEnd; i++) {
            oldVnode = oldChildren[i];
            if(patched >= count) {
              unmount(oldVnode);
              continue;
            }
            const k = keyIndex[oldVnode.key];
            if(typeof k !== "undefined") {
              newVnode = newChildren[k];
              patch(oldVnode, newVnode, container);
              source[k - newStart] = i;
              patched++;
              if(k < pos) {
                moved = true;
              } else {
                pos = k;
              }
            } else {
              unmount(oldVnode);
            }
          }
          let seq = lengthOfLIS(source); // 通过lis获取的结果是最长递增子序列的元素在source数组中位置索引, 如[2, 3, 1, -1]返回的是[0, 1];
          let s = seq.length - 1;
          for(let i = count - 1; i >= 0 ; i++) {
            let pos = newStart + i;
            let newVnode = newChildren[pos]
            let nextPos = pos + 1;
            const anchor = nextPos < newChildren.length ? newChildren[nextPos].el : null;
            if(source[i] === - 1) {
               patch(null, newVnode, container, anchor)
            } else if(moved) {
              if(seq[s] !== i) {
                insert(newVnode.el, container, anchor)
              } else {
                s--;
              }
            }
          }
        }

// 下面是自己定义的lis的实现方法

function getPos(result, num) {
  let left = 0;
  let right = result.length;
  while (left < right) {
    let mid = left + ((right - left) >> 1)
    if(num < result[mid]) {
      right = mid;
    }  else if(num > result[mid]) {
      left = mid + 1;
    } else {
      return mid;
    }
  }
  return left;
}
var lengthOfLIS = function(nums) {
  let result = [];
  let list = [];
  for(let i = 0 ; i < nums.length; i++) {
    let num = nums[i];
    if(num !== -1) {
      let pos = getPos(result, num);
      if(pos < result.length) {
        result.splice(pos,1, num)
      } else {
        list.push(i);
        result.push(num)
      }
    }
  }
  return list
};
console.log(lengthOfLIS([2, 3, 1, -1]))