更新子节点

因为VNode是一个树结构,他有很多子 Children,那么就需要依次向下遍历,
这个遍历的方法就是 patchChildren

这里比较重要

  1. const patchChildren = (
  2. n1,
  3. n2,
  4. container,
  5. anchor,
  6. parentComponent,
  7. parentSuspense,
  8. isSVG,
  9. optimized = false
  10. ) => {
  11. const c1 = n1 && n1.children
  12. const prevShapeFlag = n1 ? n1.shapeFlag : 0 // 旧节点
  13. const c2 = n2.children
  14. const { patchFlag, shapeFlag } = n2 // 新节点
  15. // fast path
  16. if (patchFlag > 0) {
  17. if (patchFlag & 128 /* KEYED_FRAGMENT */) {
  18. patchKeyedChildren(
  19. c1,
  20. c2,
  21. container,
  22. anchor,
  23. parentComponent,
  24. parentSuspense,
  25. isSVG,
  26. optimized
  27. )
  28. return
  29. } else if (patchFlag & 256 /* UNKEYED_FRAGMENT */) {
  30. patchUnkeyedChildren(
  31. c1,
  32. c2,
  33. container,
  34. anchor,
  35. parentComponent,
  36. parentSuspense,
  37. isSVG,
  38. optimized
  39. )
  40. return
  41. }
  42. }
  43. // children有3种可能:文本、数组或没有children。
  44. if (shapeFlag & 8 /* TEXT_CHILDREN */) {
  45. // text children fast path
  46. if (prevShapeFlag & 16 /* ARRAY_CHILDREN */) {
  47. // 新节点是文本,旧节点是数组,直接删除。
  48. unmountChildren(c1, parentComponent, parentSuspense)
  49. }
  50. // 新节点是文本,和旧节点不相同,直接替换。
  51. if (c2 !== c1) {
  52. hostSetElementText(container, c2)
  53. }
  54. } else {
  55. if (prevShapeFlag & 16 /* ARRAY_CHILDREN */) {
  56. if (shapeFlag & 16 /* ARRAY_CHILDREN */) {
  57. // 新节点和旧节点都是数组,diff比较
  58. patchKeyedChildren(
  59. c1,
  60. c2,
  61. container,
  62. anchor,
  63. parentComponent,
  64. parentSuspense,
  65. isSVG,
  66. optimized
  67. )
  68. } else {
  69. // 新节点不是数组,旧节点是数组,直接删除
  70. unmountChildren(c1, parentComponent, parentSuspense, true)
  71. }
  72. } else {
  73. if (prevShapeFlag & 8 /* TEXT_CHILDREN */) {
  74. // 替换空文本
  75. hostSetElementText(container, '')
  76. }
  77. // mount new if array 挂载一个新的数组子节点
  78. if (shapeFlag & 16 /* ARRAY_CHILDREN */) {
  79. mountChildren(
  80. c2,
  81. container,
  82. anchor,
  83. parentComponent,
  84. parentSuspense,
  85. isSVG,
  86. optimized
  87. )
  88. }
  89. }
  90. }
  91. }

代码中的patchKeyedChildren是根据存在的 key 进行 Diff。
代码中的patchUnKeyedChildren是根据不存在的 key 进行 Diff。

总结:

新节点是文本

  • 1 - 旧节点是数组,删除所有字节点
  • 2 - 旧节点不是是文本,替换文本
  • 3 - 旧节点是空,添加文本

新节点是数组

  • 1 - 旧节点是数组,diff比较子节点
  • 2 - 旧节点不是数组,删除所有子节点
  • 3 - 旧节点是空,挂载新的子节点

新节点是空

  • 1 - 直接删除