if (isTrue(oldVnode.isAsyncPlaceholder)) {if (isDef(vnode.asyncFactory.resolved)) {hydrate(oldVnode.elm, vnode, insertedVnodeQueue)} else {vnode.isAsyncPlaceholder = true}return}// reuse element for static trees.// note we only do this if the vnode is cloned -// if the new node is not cloned it means the render functions have been// reset by the hot-reload-api and we need to do a proper re-render.if (isTrue(vnode.isStatic) &&isTrue(oldVnode.isStatic) &&vnode.key === oldVnode.key &&(isTrue(vnode.isCloned) || isTrue(vnode.isOnce))) {vnode.componentInstance = oldVnode.componentInstancereturn}而后者的作用是进行是定义了新旧子节点数组的头和尾,然后新旧子节点数组头尾交叉对比,它只在同层级进行比较,不会跨层级比较,这是有考量的,因为前端实际的操作中,很少会把dom元素移到其他层级去 。比较完子节点之后,就开始递归调用patchVnode()更新子节点了,这里考点就来了:vue的diff算法是深度优先算法还是广度优先算法?从这个更新流程可以看出来,正常调用顺序是patch()->patchVnode()->updateChildren()->patchVnode()->updateChildren()->....这是深度优先算法,同层比较,深度优先 。
function updateChildren(parentElm,oldCh,newCh,insertedVnodeQueue,removeOnly) {let oldStartIdx = 0let newStartIdx = 0let oldEndIdx = oldCh.length - 1let oldStartVnode = oldCh[0]let oldEndVnode = oldCh[oldEndIdx]let newEndIdx = newCh.length - 1let newStartVnode = newCh[0]let newEndVnode = newCh[newEndIdx]let oldKeyToIdx, idxInOld, vnodeToMove, refElm// removeOnly is a special flag used only by <transition-group>// to ensure removed elements stay in correct relative positions// during leaving transitionsconst canMove = !removeOnlyif (__DEV__) {checkDuplicateKeys(newCh)}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 rightpatchVnode(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 leftpatchVnode(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 elementcreateElm(newStartVnode,insertedVnodeQueue,parentElm,oldStartVnode.elm,false,newCh,newStartIdx)} else {vnodeToMove = oldCh[idxInOld]if (sameVnode(vnodeToMove, newStartVnode)) {patchVnode(vnodeToMove,newStartVnode,insertedVnodeQueue,newCh,newStartIdx)oldCh[idxInOld] = undefinedcanMove &&nodeOps.insertBefore(parentElm,vnodeToMove.elm,oldStartVnode.elm)} else {// same key but different element. treat as new elementcreateElm(newStartVnode,insertedVnodeQueue,parentElm,oldStartVnode.elm,false,newCh,newStartIdx)}}newStartVnode = newCh[++newStartIdx]}}if (oldStartIdx > oldEndIdx) {refElm = isUndef(newCh[newEndIdx + 1]) ? null : newCh[newEndIdx + 1].elmaddVnodes(parentElm,refElm,newCh,newStartIdx,newEndIdx,insertedVnodeQueue)} else if (newStartIdx > newEndIdx) {removeVnodes(oldCh, oldStartIdx, oldEndIdx)}}
经验总结扩展阅读
- Ruoyi字典源码学习
- 虚拟线程 - VirtualThread源码透视
- 【Spring boot】启动过程源码分析
- 八 Netty 学习:新连接接入源码说明
- 深入底层C源码 Redis核心设计原理
- 七 Netty 学习:NioEventLoop 对应线程的创建和启动源码说明
- ERP 系统的核心是什么?有什么作用?
- Spring mvc源码分析系列--Servlet的前世今生
- spring cron表达式源码分析
- 集合框架——LinkedList集合源码分析