vue源码解析--patch算法

  1. 获取新旧vnode
    旧的vnode通过实例的_vnode属性获取
    新的vnode通过实例的$render()方法生成,同时使用另外一个变量引用instance._vnode,然后将新的vnode赋值给instance._vnode
  2. 对比新旧vnode
    1. 调用patch(oldVnode, newVnode)
    2. 如果sameVnode(oldVnode, newVnode),则调用patchVnode(oldVnode, newVnode)
    3. 根据newVnode是否为text类型,以及是否有children来进一步分析
      1. newVnode为文本节点(newVnode.text !== undefined),直接oldVnode.elm(真实dom元素).innerHtml = newVnode.text
      2. newVnode不为文本节点,
        1. 新旧Vnode都有children,调用updateChildren(parentElm, oldCh, newCh)
        2. newVnode有children(同时也意味着oldVnode没有children),将oldVnode置空,再使用addVnodes添加newVnode的children。
        3. oldVnode有children(同时也意味着newVnode没有children),使用removeVnodes移除旧的
        4. 新旧Vnode都没有children,且旧的为text,直接oldVnode.elm(真实dom元素).innerHtml = ‘’
    4. updateChildren(parentElm, oldCh, newCh)方法使用while来比较数组下面的新旧节点(这里递归使用了patchVnode)
      1. oldCh第一个Vnode和newCh第一个Vnode比较(通过sameVnode(oldVnode, newVnode)), 后续逻辑见代码
      2. oldCh最后一个Vnode和newCh最后一个Vnode比较(通过sameVnode(oldVnode, newVnode)),后续逻辑见代码
      3. oldCh第一个Vnode和newCh最后一个Vnode比较,(通过sameVnode(oldVnode, newVnode)),后续逻辑见代码
      4. oldCh最后一个Vnode和newCh第一个Vnode比较,(通过sameVnode(oldVnode, newVnode)),后续逻辑见代码
      5. 执行到这里说明以上不适配,所以将oldCh的数组用一个对象来描述
      	a = {
      		//key: index
      		weiyikey1: 1,
      		weiyikey2: 2,
      		weiyikey3: 3
      	}
      
      newCh里面Vnode是否满足 Vnode.key in a,满足则可以使用patchVnode(),然后在移动真实dom(基本上都是使用originNode.insertBefore(newVnode, oldVnode)),不满足则说明不能复用旧dom,简单的全部新增newVnode,全部移除oldVnode。

流程图如下(通过chrome截取)
在这里插入图片描述

function patch (oldVnode, vnode, hydrating, removeOnly) {
...
      if (!isRealElement && sameVnode(oldVnode, vnode)) {
          // patch existing root node
          patchVnode(oldVnode, vnode, insertedVnodeQueue, null, null, removeOnly);
      }
'''
}

function patchVnode (
      oldVnode,
      vnode,
      insertedVnodeQueue,
      ownerArray,
      index,
      removeOnly
    ) {
    ...
	if (isUndef(vnode.text)) {
      if (isDef(oldCh) && isDef(ch)) {
        if (oldCh !== ch) { updateChildren(elm, oldCh, ch, insertedVnodeQueue, removeOnly); }
      } else if (isDef(ch)) {
        {
          checkDuplicateKeys(ch);
        }
        if (isDef(oldVnode.text)) { nodeOps.setTextContent(elm, ''); }
        addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue);
      } else if (isDef(oldCh)) {
        removeVnodes(elm, oldCh, 0, oldCh.length - 1);
      } else if (isDef(oldVnode.text)) {
        nodeOps.setTextContent(elm, '');
      }
    } else if (oldVnode.text !== vnode.text) {
      nodeOps.setTextContent(elm, vnode.text);
    }
    ...
}

function updateChildren (parentElm, oldCh, newCh, insertedVnodeQueue, removeOnly) {
	while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
        if (isUndef(oldStartVnode)) {
          oldStartVnode = oldCh[++oldStartIdx]; // Vnode has been moved left
        } else if (isUndef(oldEndVnode)) {
          oldEndVnode = oldCh[--oldEndIdx];
        } else if (sameVnode(oldStartVnode, newStartVnode)) {
          patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue, newCh, newStartIdx);
          oldStartVnode = oldCh[++oldStartIdx];
          newStartVnode = newCh[++newStartIdx];
        } else if (sameVnode(oldEndVnode, newEndVnode)) {
          patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue, newCh, newEndIdx);
          oldEndVnode = oldCh[--oldEndIdx];
          newEndVnode = newCh[--newEndIdx];
        } else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved right
          patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue, newCh, newEndIdx);
          canMove && nodeOps.insertBefore(parentElm, oldStartVnode.elm, nodeOps.nextSibling(oldEndVnode.elm));
          oldStartVnode = oldCh[++oldStartIdx];
          newEndVnode = newCh[--newEndIdx];
        } else if (sameVnode(oldEndVnode, newStartVnode)) { // Vnode moved left
          patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue, newCh, newStartIdx);
          canMove && nodeOps.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm);
          oldEndVnode = oldCh[--oldEndIdx];
          newStartVnode = newCh[++newStartIdx];
        } else {
          if (isUndef(oldKeyToIdx)) { oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx); }
          idxInOld = isDef(newStartVnode.key)
            ? oldKeyToIdx[newStartVnode.key]
            : findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx);
          if (isUndef(idxInOld)) { // New element
            createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx);
          } else {
            vnodeToMove = oldCh[idxInOld];
            if (sameVnode(vnodeToMove, newStartVnode)) {
              patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue, newCh, newStartIdx);
              oldCh[idxInOld] = undefined;
              canMove && nodeOps.insertBefore(parentElm, vnodeToMove.elm, oldStartVnode.elm);
            } else {
              // same key but different element. treat as new element
              createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx);
            }
          }
          newStartVnode = newCh[++newStartIdx];
        }
      }
      if (oldStartIdx > oldEndIdx) {
        refElm = isUndef(newCh[newEndIdx + 1]) ? null : newCh[newEndIdx + 1].elm;
        addVnodes(parentElm, refElm, newCh, newStartIdx, newEndIdx, insertedVnodeQueue);
      } else if (newStartIdx > newEndIdx) {
        removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx);
      }
   }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值