vue 2.x
    老习惯,先贴链接
    https://www.zhihu.com/collection/643450781 vue3源码
    https://zhuanlan.zhihu.com/p/362539108
    https://ustbhuangyi.github.io/vue-analysis/v2/reactive/component-update.html#updatechildren 例子
    目的,减少重复创建的开销,旧节点的移动+新节点的插入
    当收尾/交叉比较条件不满足时,会拿newStartVnode去oldCh的key-idx映射里去寻找是否存在idx,不存在/
    存在但是内容不同的情况,则会进入一个含有插入行为的create方法里:
    createElm(newStartVnode, _insertedVnodeQueue_, _parentElm_, oldStartVnode.elm, false, _newCh_, newStartIdx),
    ->insert(_parentElm_, newStartVnode.elm, oldStartVnode.elm)
    ->node.insertBefore(parentEle, newEle, referanceEle)

    1. function insert (parent, elm, ref) {
    2. if (isDef(parent)) {
    3. if (isDef(ref)) {
    4. if (nodeOps.parentNode(ref) === parent) {
    5. nodeOps.insertBefore(parent, elm, ref)
    6. }
    7. } else {
    8. nodeOps.appendChild(parent, elm)
    9. }
    10. }
    11. }
    1. function updateChildren (parentElm, oldCh, newCh, insertedVnodeQueue, removeOnly) {
    2. let oldStartIdx = 0
    3. let newStartIdx = 0
    4. let oldEndIdx = oldCh.length - 1
    5. let oldStartVnode = oldCh[0]
    6. let oldEndVnode = oldCh[oldEndIdx]
    7. let newEndIdx = newCh.length - 1
    8. let newStartVnode = newCh[0]
    9. let newEndVnode = newCh[newEndIdx]
    10. let oldKeyToIdx, idxInOld, vnodeToMove, refElm
    11. // removeOnly is a special flag used only by <transition-group>
    12. // to ensure removed elements stay in correct relative positions
    13. // during leaving transitions
    14. const canMove = !removeOnly
    15. if (process.env.NODE_ENV !== 'production') {
    16. checkDuplicateKeys(newCh)
    17. }
    18. while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
    19. if (isUndef(oldStartVnode)) {
    20. oldStartVnode = oldCh[++oldStartIdx] // Vnode has been moved left
    21. } else if (isUndef(oldEndVnode)) {
    22. oldEndVnode = oldCh[--oldEndIdx]
    23. } else if (sameVnode(oldStartVnode, newStartVnode)) {
    24. patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue, newCh, newStartIdx)
    25. oldStartVnode = oldCh[++oldStartIdx]
    26. newStartVnode = newCh[++newStartIdx]
    27. } else if (sameVnode(oldEndVnode, newEndVnode)) {
    28. patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue, newCh, newEndIdx)
    29. oldEndVnode = oldCh[--oldEndIdx]
    30. newEndVnode = newCh[--newEndIdx]
    31. } else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved right
    32. patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue, newCh, newEndIdx)
    33. canMove && nodeOps.insertBefore(parentElm, oldStartVnode.elm, nodeOps.nextSibling(oldEndVnode.elm))
    34. oldStartVnode = oldCh[++oldStartIdx]
    35. newEndVnode = newCh[--newEndIdx]
    36. } else if (sameVnode(oldEndVnode, newStartVnode)) { // Vnode moved left
    37. patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue, newCh, newStartIdx)
    38. // node target before
    39. canMove && nodeOps.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm)
    40. oldEndVnode = oldCh[--oldEndIdx]
    41. newStartVnode = newCh[++newStartIdx]
    42. } else {
    43. if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx)
    44. idxInOld = isDef(newStartVnode.key)
    45. ? oldKeyToIdx[newStartVnode.key]
    46. : findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx)
    47. if (isUndef(idxInOld)) { // New element
    48. createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx)
    49. } else {
    50. vnodeToMove = oldCh[idxInOld]
    51. if (sameVnode(vnodeToMove, newStartVnode)) {
    52. patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue, newCh, newStartIdx)
    53. oldCh[idxInOld] = undefined
    54. canMove && nodeOps.insertBefore(parentElm, vnodeToMove.elm, oldStartVnode.elm)
    55. } else {
    56. // same key but different element. treat as new element
    57. //
    58. createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx)
    59. }
    60. }
    61. newStartVnode = newCh[++newStartIdx]
    62. }
    63. }
    64. if (oldStartIdx > oldEndIdx) {
    65. refElm = isUndef(newCh[newEndIdx + 1]) ? null : newCh[newEndIdx + 1].elm
    66. addVnodes(parentElm, refElm, newCh, newStartIdx, newEndIdx, insertedVnodeQueue)
    67. } else if (newStartIdx > newEndIdx) {
    68. removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx)
    69. }
    70. }
    71. function createElm (
    72. vnode,
    73. insertedVnodeQueue,
    74. parentElm,
    75. refElm,
    76. nested,
    77. ownerArray,
    78. index
    79. ) {
    80. if (isDef(vnode.elm) && isDef(ownerArray)) {
    81. // This vnode was used in a previous render!
    82. // now it's used as a new node, overwriting its elm would cause
    83. // potential patch errors down the road when it's used as an insertion
    84. // reference node. Instead, we clone the node on-demand before creating
    85. // associated DOM element for it.
    86. vnode = ownerArray[index] = cloneVNode(vnode)
    87. }
    88. vnode.isRootInsert = !nested // for transition enter check
    89. if (createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) {
    90. return
    91. }
    92. const data = vnode.data
    93. const children = vnode.children
    94. const tag = vnode.tag
    95. if (isDef(tag)) {
    96. if (process.env.NODE_ENV !== 'production') {
    97. if (data && data.pre) {
    98. creatingElmInVPre++
    99. }
    100. if (isUnknownElement(vnode, creatingElmInVPre)) {
    101. warn(
    102. 'Unknown custom element: <' + tag + '> - did you ' +
    103. 'register the component correctly? For recursive components, ' +
    104. 'make sure to provide the "name" option.',
    105. vnode.context
    106. )
    107. }
    108. }
    109. vnode.elm = vnode.ns
    110. ? nodeOps.createElementNS(vnode.ns, tag)
    111. : nodeOps.createElement(tag, vnode)
    112. setScope(vnode)
    113. /* istanbul ignore if */
    114. if (__WEEX__) {
    115. // in Weex, the default insertion order is parent-first.
    116. // List items can be optimized to use children-first insertion
    117. // with append="tree".
    118. const appendAsTree = isDef(data) && isTrue(data.appendAsTree)
    119. if (!appendAsTree) {
    120. if (isDef(data)) {
    121. invokeCreateHooks(vnode, insertedVnodeQueue)
    122. }
    123. insert(parentElm, vnode.elm, refElm)
    124. }
    125. createChildren(vnode, children, insertedVnodeQueue)
    126. if (appendAsTree) {
    127. if (isDef(data)) {
    128. invokeCreateHooks(vnode, insertedVnodeQueue)
    129. }
    130. insert(parentElm, vnode.elm, refElm)
    131. }
    132. } else {
    133. createChildren(vnode, children, insertedVnodeQueue)
    134. if (isDef(data)) {
    135. invokeCreateHooks(vnode, insertedVnodeQueue)
    136. }
    137. insert(parentElm, vnode.elm, refElm)
    138. }
    139. if (process.env.NODE_ENV !== 'production' && data && data.pre) {
    140. creatingElmInVPre--
    141. }
    142. } else if (isTrue(vnode.isComment)) {
    143. vnode.elm = nodeOps.createComment(vnode.text)
    144. insert(parentElm, vnode.elm, refElm)
    145. } else {
    146. vnode.elm = nodeOps.createTextNode(vnode.text)
    147. insert(parentElm, vnode.elm, refElm)
    148. }
    149. }
    150. //createElement也可以负责插入职责
    151. function insert (parent, elm, ref) {
    152. if (isDef(parent)) {
    153. if (isDef(ref)) {
    154. if (nodeOps.parentNode(ref) === parent) {
    155. nodeOps.insertBefore(parent, elm, ref)
    156. }
    157. } else {
    158. nodeOps.appendChild(parent, elm)
    159. }
    160. }
    161. }