vue中把Diff的过程叫做patch过程,patch的过程就是以新的VNode为基准,去更新旧的VNode
    patch过程中,如果面对当前VNode存在有很多chidren的情况,那么需要分别遍历patch新的children Vnode和老的 children VNode。

    patch 源码:

    1. // 判断n1和n2是否是相似节点,type一样 并且 key一样
    2. function isSameVNodeType(n1, n2) {
    3. if (n2.shapeFlag & 6 /* COMPONENT */ && hmrDirtyComponents.has(n2.type)) {
    4. return false
    5. }
    6. return n1.type === n2.type && n1.key === n2.key
    7. }
    1. const patch = (
    2. n1, // 旧vnode
    3. n2, // 新vnode
    4. container,
    5. anchor = null, // 定位锚点dom,用于往锚点前插节点
    6. parentComponent = null,
    7. parentSuspense = null,
    8. isSVG = false,
    9. optimized = false // 是否启用diff优化
    10. ) => {
    11. // 存在旧的VNode,并且新的VNode类型(key|type)与之不同,销毁对应的旧结点
    12. if (n1 && !isSameVNodeType(n1, n2)) {
    13. anchor = getNextHostNode(n1);
    14. //n1设置为null,保证后面走整个节点的mount逻辑
    15. n1 = null;
    16. }
    17. if (n2.patchFlag === -2 /* BAIL */) {
    18. optimized = false;
    19. n2.dynamicChildren = null;
    20. }
    21. /**
    22. patch内部有两种情况:
    23. 1. 挂载 n1 为null
    24. 2. 更新
    25. */
    26. const { type, ref, shapeFlag } = n2;
    27. switch (type) {
    28. case Text: //处理文本
    29. processText(n1, n2, container, anchor);
    30. break;
    31. case Comment: //处理注释
    32. processCommentNode(n1, n2, container, anchor);
    33. break;
    34. case Static: //处理静态节点
    35. if (n1 == null) {
    36. mountStaticNode(n2, container, anchor, isSVG);
    37. }
    38. else {
    39. patchStaticNode(n1, n2, container, isSVG);
    40. }
    41. break;
    42. case Fragment: //处理Fragment元素</>
    43. processFragment(n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, optimized);
    44. break;
    45. default: //elemment 处理DOM元素
    46. if (shapeFlag & 1 /* ELEMENT */) {
    47. // 处理element
    48. processElement(n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, optimized);
    49. }
    50. else if (shapeFlag & 6 /* COMPONENT */) {
    51. // 处理组件
    52. processComponent(n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, optimized);
    53. }
    54. else if (shapeFlag & 64 /* TELEPORT */) {
    55. // 处理teleport
    56. type.process(n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, optimized, internals);
    57. }
    58. else if ( shapeFlag & 128 /* SUSPENSE */) {
    59. // 处理 suspense
    60. type.process(n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, optimized, internals);
    61. }
    62. else {
    63. warn('Invalid VNode type:', type, `(${typeof type})`);
    64. }
    65. }
    66. // set ref
    67. if (ref != null && parentComponent) {
    68. setRef(ref, n1 && n1.ref, parentSuspense, n2);
    69. }
    70. }

    解释:
    n1 旧节点;
    n2 新节点;

    总结:

    1. n1 和 n2 不是相似节点,设置n1=null,在子方法标识是新节点,走mount而不是更新。
    2. 判断 n2 类型,执行对应节点的操作方法
      1. text 文本
      2. Comment 注释
      3. Static 静态节点
      4. Fragment 碎片节点
      5. Element
      6. Component 组件或函数组件
      7. Teleport
      8. Suspense