vue中把Diff的过程叫做patch过程,patch的过程就是以新的VNode为基准,去更新旧的VNode。
patch过程中,如果面对当前VNode存在有很多chidren的情况,那么需要分别遍历patch新的children Vnode和老的 children VNode。
patch 源码:
// 判断n1和n2是否是相似节点,type一样 并且 key一样function isSameVNodeType(n1, n2) {if (n2.shapeFlag & 6 /* COMPONENT */ && hmrDirtyComponents.has(n2.type)) {return false}return n1.type === n2.type && n1.key === n2.key}
const patch = (n1, // 旧vnoden2, // 新vnodecontainer,anchor = null, // 定位锚点dom,用于往锚点前插节点parentComponent = null,parentSuspense = null,isSVG = false,optimized = false // 是否启用diff优化) => {// 存在旧的VNode,并且新的VNode类型(key|type)与之不同,销毁对应的旧结点if (n1 && !isSameVNodeType(n1, n2)) {anchor = getNextHostNode(n1);//n1设置为null,保证后面走整个节点的mount逻辑n1 = null;}if (n2.patchFlag === -2 /* BAIL */) {optimized = false;n2.dynamicChildren = null;}/**patch内部有两种情况:1. 挂载 n1 为null2. 更新*/const { type, ref, shapeFlag } = n2;switch (type) {case Text: //处理文本processText(n1, n2, container, anchor);break;case Comment: //处理注释processCommentNode(n1, n2, container, anchor);break;case Static: //处理静态节点if (n1 == null) {mountStaticNode(n2, container, anchor, isSVG);}else {patchStaticNode(n1, n2, container, isSVG);}break;case Fragment: //处理Fragment元素</>processFragment(n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, optimized);break;default: //elemment 处理DOM元素if (shapeFlag & 1 /* ELEMENT */) {// 处理elementprocessElement(n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, optimized);}else if (shapeFlag & 6 /* COMPONENT */) {// 处理组件processComponent(n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, optimized);}else if (shapeFlag & 64 /* TELEPORT */) {// 处理teleporttype.process(n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, optimized, internals);}else if ( shapeFlag & 128 /* SUSPENSE */) {// 处理 suspensetype.process(n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, optimized, internals);}else {warn('Invalid VNode type:', type, `(${typeof type})`);}}// set refif (ref != null && parentComponent) {setRef(ref, n1 && n1.ref, parentSuspense, n2);}}
解释:n1 旧节点;n2 新节点;
总结:
- n1 和 n2 不是相似节点,设置n1=null,在子方法标识是新节点,走
mount而不是更新。 - 判断 n2 类型,执行对应节点的操作方法
- text 文本
- Comment 注释
- Static 静态节点
- Fragment 碎片节点
- Element
- Component 组件或函数组件
- Teleport
- Suspense
