You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Vue.prototype._update=function(vnode: VNode,hydrating?: boolean){constvm: Component=thisconstprevEl=vm.$elconstprevVnode=vm._vnodeconstrestoreActiveInstance=setActiveInstance(vm)vm._vnode=vnode// Vue.prototype.__patch__ is injected in entry points// based on the rendering backend used.if(!prevVnode){// initial rendervm.$el=vm.__patch__(vm.$el,vnode,hydrating,false/* removeOnly */)}else{// updatesvm.$el=vm.__patch__(prevVnode,vnode)}restoreActiveInstance()// update __vue__ referenceif(prevEl){prevEl.__vue__=null}if(vm.$el){vm.$el.__vue__=vm}// if parent is an HOC, update its $el as wellif(vm.$vnode&&vm.$parent&&vm.$vnode===vm.$parent._vnode){vm.$parent.$el=vm.$el}// updated hook is called by the scheduler to ensure that children are// updated in a parent's updated hook.}
patch
functionpatch(oldVnode,vnode,hydrating,removeOnly){if(isUndef(vnode)){if(isDef(oldVnode))invokeDestroyHook(oldVnode)return}letisInitialPatch=falseconstinsertedVnodeQueue=[]if(isUndef(oldVnode)){// empty mount (likely as component), create new root elementisInitialPatch=truecreateElm(vnode,insertedVnodeQueue)}else{constisRealElement=isDef(oldVnode.nodeType)if(!isRealElement&&sameVnode(oldVnode,vnode)){// patch existing root nodepatchVnode(oldVnode,vnode,insertedVnodeQueue,null,null,removeOnly)}else{if(isRealElement){// mounting to a real element// check if this is server-rendered content and if we can perform// a successful hydration.if(oldVnode.nodeType===1&&oldVnode.hasAttribute(SSR_ATTR)){oldVnode.removeAttribute(SSR_ATTR)hydrating=true}if(isTrue(hydrating)){if(hydrate(oldVnode,vnode,insertedVnodeQueue)){invokeInsertHook(vnode,insertedVnodeQueue,true)returnoldVnode}elseif(process.env.NODE_ENV!=='production'){warn('The client-side rendered virtual DOM tree is not matching '+'server-rendered content. This is likely caused by incorrect '+'HTML markup, for example nesting block-level elements inside '+'<p>, or missing <tbody>. Bailing hydration and performing '+'full client-side render.')}}// either not server-rendered, or hydration failed.// create an empty node and replace itoldVnode=emptyNodeAt(oldVnode)}// replacing existing elementconstoldElm=oldVnode.elmconstparentElm=nodeOps.parentNode(oldElm)// create new nodecreateElm(vnode,insertedVnodeQueue,// extremely rare edge case: do not insert if old element is in a// leaving transition. Only happens when combining transition +// keep-alive + HOCs. (#4590)oldElm._leaveCb ? null : parentElm,nodeOps.nextSibling(oldElm))// update parent placeholder node element, recursivelyif(isDef(vnode.parent)){letancestor=vnode.parentconstpatchable=isPatchable(vnode)while(ancestor){for(leti=0;i<cbs.destroy.length;++i){cbs.destroy[i](ancestor)}ancestor.elm=vnode.elmif(patchable){for(leti=0;i<cbs.create.length;++i){cbs.create[i](emptyNode,ancestor)}// #6513// invoke insert hooks that may have been merged by create hooks.// e.g. for directives that uses the "inserted" hook.constinsert=ancestor.data.hook.insertif(insert.merged){// start at index 1 to avoid re-invoking component mounted hookfor(leti=1;i<insert.fns.length;i++){insert.fns[i]()}}}else{registerRef(ancestor)}ancestor=ancestor.parent}}// destroy old nodeif(isDef(parentElm)){removeVnodes([oldVnode],0,0)}elseif(isDef(oldVnode.tag)){invokeDestroyHook(oldVnode)}}}invokeInsertHook(vnode,insertedVnodeQueue,isInitialPatch)returnvnode.elm}
patchVnode
functionpatchVnode(oldVnode,vnode,insertedVnodeQueue,ownerArray,index,removeOnly){if(oldVnode===vnode){return}if(isDef(vnode.elm)&&isDef(ownerArray)){// clone reused vnodevnode=ownerArray[index]=cloneVNode(vnode)}constelm=vnode.elm=oldVnode.elmif(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}leticonstdata=vnode.dataif(isDef(data)&&isDef(i=data.hook)&&isDef(i=i.prepatch)){i(oldVnode,vnode)}constoldCh=oldVnode.childrenconstch=vnode.childrenif(isDef(data)&&isPatchable(vnode)){for(i=0;i<cbs.update.length;++i)cbs.update[i](oldVnode,vnode)if(isDef(i=data.hook)&&isDef(i=i.update))i(oldVnode,vnode)}if(isUndef(vnode.text)){if(isDef(oldCh)&&isDef(ch)){if(oldCh!==ch)updateChildren(elm,oldCh,ch,insertedVnodeQueue,removeOnly)}elseif(isDef(ch)){if(process.env.NODE_ENV!=='production'){checkDuplicateKeys(ch)}if(isDef(oldVnode.text))nodeOps.setTextContent(elm,'')addVnodes(elm,null,ch,0,ch.length-1,insertedVnodeQueue)}elseif(isDef(oldCh)){removeVnodes(oldCh,0,oldCh.length-1)}elseif(isDef(oldVnode.text)){nodeOps.setTextContent(elm,'')}}elseif(oldVnode.text!==vnode.text){nodeOps.setTextContent(elm,vnode.text)}if(isDef(data)){if(isDef(i=data.hook)&&isDef(i=i.postpatch))i(oldVnode,vnode)}}
updateChildren
functionupdateChildren(parentElm,oldCh,newCh,insertedVnodeQueue,removeOnly){letoldStartIdx=0letnewStartIdx=0letoldEndIdx=oldCh.length-1letoldStartVnode=oldCh[0]letoldEndVnode=oldCh[oldEndIdx]letnewEndIdx=newCh.length-1letnewStartVnode=newCh[0]letnewEndVnode=newCh[newEndIdx]letoldKeyToIdx,idxInOld,vnodeToMove,refElm// removeOnly is a special flag used only by <transition-group>// to ensure removed elements stay in correct relative positions// during leaving transitionsconstcanMove=!removeOnlyif(process.env.NODE_ENV!=='production'){checkDuplicateKeys(newCh)}while(oldStartIdx<=oldEndIdx&&newStartIdx<=newEndIdx){if(isUndef(oldStartVnode)){oldStartVnode=oldCh[++oldStartIdx]// Vnode has been moved left}elseif(isUndef(oldEndVnode)){oldEndVnode=oldCh[--oldEndIdx]}elseif(sameVnode(oldStartVnode,newStartVnode)){patchVnode(oldStartVnode,newStartVnode,insertedVnodeQueue,newCh,newStartIdx)oldStartVnode=oldCh[++oldStartIdx]newStartVnode=newCh[++newStartIdx]}elseif(sameVnode(oldEndVnode,newEndVnode)){patchVnode(oldEndVnode,newEndVnode,insertedVnodeQueue,newCh,newEndIdx)oldEndVnode=oldCh[--oldEndIdx]newEndVnode=newCh[--newEndIdx]}elseif(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]}elseif(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)}elseif(newStartIdx>newEndIdx){removeVnodes(oldCh,oldStartIdx,oldEndIdx)}}
The text was updated successfully, but these errors were encountered:
整体流程
文字描述
概括来说,vue中第一次创建组件或者组件数据更新时,会进入
update
方法,在update
方法中会判断如果是第一次创建就直接创建新节点并插入到DOM中,如果不是第一次就会进入patch
方法;在patch
中会对比新旧节点,如果不同就创建新节点并插入DOM还有删除旧节点,如果相同就进入patchVnode
;在patchVnode
中会判断新旧节点是不是文本节点,如果是就对比文本内容,不一样就修改掉,如果不是文本内容就看新旧节点有没有子节点,如果新节点存在子节点而旧节点没有,就去添加子节点,反之就去删除子节点,如果都有子节点就会进入到updateChildren
(关键步骤),在该方法中会对比新旧节点children中的每一个成员节点,先是比较两组children中firstChild(sameVnode(oldStartVnode, newStartVnode)
),然后lastChild(sameVnode(oldEndVnode, newEndVnode)
),还有首尾交叉比较(sameVnode(oldStartVnode, newEndVnode)
,sameVnode(oldEndVnode, newStartVnode)
),如果都不一样,再进行first和last比较,还不一样就进入终极比较——通过key挨个每一个child节点,说一下这个key的原理:通过新节点和旧节点的children生成两个以该节点的key为key的object,然后对比两个child节点的话,就可以拿着一个节点的key在另外一个object中查找,如果没有就说明该节点在另一个children中不存在,如果存在就用
sameVnode
对比两个节点,这样就能很快地结束一个节点的对比;这里如果child节点没有key的话,在对比的时候就会通过该节点在另外一组children中循环使用sameVnode
对比,那很明显,如果子节点比较多的时候就会很明显的出现性能问题,所以在开发过程中使用模板for循环时一定不要忘记带上一个key。在使用
sameVnode
对比完child节点,如果结果为true
,就会再次调用patchVnode
,然后如果不是首首或者尾尾之间的比较,就会直接把旧的child节点的DOM元素插入到页面的DOM中,结果为false
,就使用新的child节点创建一个新DOM元素插入到页面DOM中,然后在页面DOM中删除旧child,那要是首或者尾尾之间的比较,使用sameVnode
对比结果为true
,就不用进行任何操作,只要往下接着对比剩下的child节点就可以了。代码实现
update
patch
patchVnode
updateChildren
The text was updated successfully, but these errors were encountered: