VUE3源码-Compare

目录

前言

旧vNode 和 新 vNode 的对比

主要是判断是否是同一vNodeType

两个列表【Children】的对比

有设置key值的对比

 没有设置key值的对比

总结


前言

本章节,我们将一起阅读源码,观看VUE3底层对vNode和列表的比较是怎么进行的,以及为什么列表需要设置key值。

旧vNode 和 新 vNode 的对比

主要是判断是否是同一vNodeType

    function isSameVNodeType(n1, n2) {
      ...
      return n1.type === n2.type && n1.key === n2.key;
    }

type: div、button、span等标签,也可能是当它为组件时,是object

key:就是 :key 的值, 例如:  <button :key=12> 中 key 就是12

两个列表【Children】的对比

有设置key值的对比

  • 第一步,从头部(i=0)开始,比较到不同的地方,break出来

          let i = 0;
          const l2 = c2.length;
          let e1 = c1.length - 1; // prev ending index
          let e2 = l2 - 1; // next ending index
          // 1. sync from start
          // (a b) c
          // (a b) d e
          while (i <= e1 && i <= e2) {
              const n1 = c1[i];
              const n2 = c2[i];
              if (相同) { 挂载}
              else { break;}
              i++;
          }
  • 第二步,这时i的值为前面不同点处,再从尾部(i=length-1)开始比较到不同之处,break出来。

          // 2. sync from end
          // a (b c)
          // d e (b c)
          while (i <= e1 && i <= e2) {
              const n1 = c1[e1];
              const n2 = c2[e2];
              if (相同) {挂载}
              else {break;}
              e1--;
              e2--;
          }
  • 第三步,前两步主要是把范围向内缩小,当两边已经相遇的时候,新列表 e2 又比 旧列表e1 多一些数据,即列表新增了几条数据的话,直接挂载这部分的数据
          // 3. common sequence + mount
          // (a b)
          // (a b) c
          // i = 2, e1 = 1, e2 = 2
          // (a b)
          // c (a b)
          // i = 0, e1 = -1, e2 = 0
          if (i > e1) {
              if (i <= e2) {
                  const nextPos = e2 + 1;
                  const anchor = nextPos < l2 ? c2[nextPos].el : parentAnchor;
                  while (i <= e2) {
                      patch(null, (c2[i] = optimized
                          ? cloneIfMounted(c2[i])
                          : normalizeVNode(c2[i])), container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized);
                      i++;
                  }
              }
          }
  • 第四步,当两边已经相遇的时候,新列表 e2 又比 旧列表e1 少一些数据,即列表删除了几条数据的话,直接卸载这部分的数据
          // 4. common sequence + unmount
          // (a b) c
          // (a b)
          // i = 2, e1 = 2, e2 = 1
          // a (b c)
          // (b c)
          // i = 0, e1 = 0, e2 = -1
          else if (i > e2) {
              while (i <= e1) {
                  unmount(c1[i], parentComponent, parentSuspense, true);
                  i++;
              }
          }
  • 第五步,第一步和第二步是收敛,第三步和第四步是处理已经相遇的情况,那么第五步,就是处理没有相遇的情况

1. 计算还应挂载多少个child,然后新建一个,遍历新数据设置 keyToNewIndexMap;

2.新建newIndexToOldIndexMap变量,遍历旧数据列表,如果存在在新的列表中,就挂载,设置newIndexToOldIndexMap为新的值;如果查询不到,或者已经足够挂载数了,就直接卸载掉;

              const newIndexToOldIndexMap = new Array(toBePatched);
              for (i = 0; i < toBePatched; i++)
                  newIndexToOldIndexMap[i] = 0;
              for (i = s1; i <= e1; i++) {//遍历旧数据列表
                  const prevChild = c1[i];
                  if (patched >= toBePatched) {
                      //已经达到挂载数了,其他卸载掉
                      unmount(...);
                      continue;
                  }
                  let newIndex; 
                  //todo 根据 keyToNewIndexMap(或者遍历判断isSameVNodeType),获取newIndex
                  ...
                  if (newIndex === undefined) { 
                      //不存在新的列表中了
                      unmount(...);
                  }
                  else {
                     //存在挂载,设置maxNewIndexSoFar或标记move,为了后续move的时候做处理
                      newIndexToOldIndexMap[newIndex - s2] = i + 1;
                      if (newIndex >= maxNewIndexSoFar) { maxNewIndexSoFar = newIndex;}
                      else { moved = true;}
                      patch(prevChild, c2[newIndex], container, null, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized);
                      patched++;
                  }
              }

3.根据newIndexToOldIndexMap出处理新队列,判断是新增的就新增,或者对dom做move处理。

 没有设置key值的对比

这块没有key值对比就比较粗暴,把之前位置一对一比较,直接挂载(patch:patch内部会做isSameVNodeType比较,不一样就卸载);长度进行比较。多了挂载,少的卸载

const patchUnkeyedChildren = (c1, c2, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized) => {
          c1 = c1 || EMPTY_ARR;
          c2 = c2 || EMPTY_ARR;
          const oldLength = c1.length;
          const newLength = c2.length;
          const commonLength = Math.min(oldLength, newLength);
          let i;
          for (i = 0; i < commonLength; i++) {
              ...
              patch(c1[i]...);
          }
          if (oldLength > newLength) {
              // remove old
              unmountChildren(c1, parentComponent, parentSuspense, true, false, commonLength);
          }
          else {
              // mount new
              mountChildren(c2, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized, commonLength);
          }
      };

总结

经过本章节的说明,其实可以看出列表设置键值key的重要性,而且key值应是独一的,具有个人标识符,例如:id;这样在列表的比较的时候,可以更好的复用之前的vNode。当只是移动、删除时,可以仅更改到当前vNode。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值