https://www.processon.com/vie...当触发响应式触发器时,高效完成重新渲染页面的过程。diff算法做的是比较vnode和oldVnode,然后在oldVnode做小改动的情况下以VNode为标准,完成VNode对应的dom渲染,返回到之前的_updated方法Vue.prototype._update=function(vnode){constvm=thisconstprevVnode=vm._vnodevm._vnode=vnode//像以前一样缓存vnodeif(!prevVnode){//首先渲染vm.$el=vm.__patch__(vm.$el,vnode)}else{//重新渲染vm.$el=vm.__patch__(prevVnode,vnode)}}patch只需要做三件事:创建新节点,删除废弃节点,更新现有节点,创建新节点node,有两种情况:VNode中有node,oldVNode没有,可以把VNode渲染成真正的dom插入进去。VNode和oldVNode不是同一个节点。直接将VNode渲染成真实的dom,插入到旧节点后面,删除旧节点,判断两个节点是否是同一个节点functionsameVnode(a,b){//是否是同一个VNode节点return(a.key===b.key&&(//和v-for(a.tag===b.tag&&//tag一样a.isComment===b.isComment&&//注释节点isDef(a.data)===isDef(b.data)&&//两者都有数据属性sameInputType(a,b)//相同的输入类型)||(isTrue(a.isAsyncPlaceholder)&&//是异步的占位符节点a.asyncFactory===b.asyncFactory&&//异步工厂方法isUndef(b.asyncFactory.error)))}删除废弃节点,比较vnode和oldvnode。如果根节点不一样,就把VNode渲染成真正的dom插入到老节点后面。删除被废除的旧节点if(isDef(parentElm)){//删除其父节点中的旧节点removeVnodes(parentElm,[oldVnode],0,0)}functionremoveVnodes(parentElm,vnodes,startIdx,endIdx){for(;startIdx<=endIdx;++startIdx){constch=vnodes[startIdx]if(isDef(ch)){removeNode(ch.elm)}}}//移除startIdx到endIdx的内容------------------------------------------------------------functionremoveNode(el){//移除单个节点constparent=nodeOps.parentNode(el)if(isDef(parent)){nodeOps.removeChild(parent,el)}}更新现有节点当两个节点是同一个节点,需要找出它们的不同点。patchVnode方法是一个静态节点.key){//所有静态节点跳过vnode.componentInstance=oldVnode.comn??intInstancereturn}vnodenodehasnotextattribute//3.vnodehasnotextattributeif(isUndef(vnode.text)){//两边都有子节点:比较子节点~~~~if(isDef(oldCh)&&isDef(ch)){if(oldCh!==ch)//children不同更新子节点updateChildren(elm,oldCh,ch,insertedVnodeQueue,removeOnly)}elseif(isDef(ch)){//只有Vnode有子节点nodesif(isDef(oldVnode.text)){//oldnode有一个文本节点,将oldvnode设置为空nodeOps.setTextContent(elm,'')}//将vnodechildren的真实dom插入到oldvnode的空标签中addVnodes(elm,null,ch,0,ch.length-1,insertedVnodeQueue)}elseif(isDef(oldCh)){//只有oldvnode有孩子,移除所有removeVnodes(oldCh,0,oldCh.length-1)}elseif(isDef(oldVnode.text)){//oldVnode有一个文本节点设置为空~~~~nodeOps.setTextContent(elm,'')}}elseif(oldVnode.text!==vnode.text){//文本更新nodeOps.setTextContent(elm,vnode.text)}如果vnode没有文本节点,则有wi将有四个分支,有不同的孩子。使用updateChildren方法更详细地比较他们的孩子。只有vnode有children,清除oldvnode,创建vnode的children作为realdominsertsoldvnodeonlyhasoldvnodewithchildren,clearoldvnodeonlyoldvnodehastext,clearoldvnode总结:vnode是标准的,所以如果vnode有文本节点,不管oldvnode是什么类型的节点,直接在vnode中设置文本更新childnodeupdateChildren()updateChildren的主要功能是通过一种有效的方式比较新旧vnode的子节点以获得最小操作补丁。新旧vnode节点左右各有一个变量标记。这两个参数会在遍历过程中显示出来。喜欢中间关闭。当oldStartIdx>oldEndIdx或newStartIdx>newEndIdx时,结束循环函数updateChildren(parentElm,oldCh,newCh,insertedVnodeQueue,removeOnly){letoldStartIdx=0//旧vnode的第一个下标letnewStartIdx=0//新vnode的第一个subscriptletoldEndIdx=oldCh.length-1//旧vnode的最后一个下标letoldStartVnode=oldCh[0]//旧vnode的第一个节点letoldEndVnode=oldCh[oldEndIdx]//旧vnode的最后一个节点letnewEndIdx=newCh.length-1//新vnode的最后一个下标letnewStartVnode=newCh[0]//新vnode的第一个节点letnewEndVnode=newCh[newEndIdx]//新vnode的第一个节点letoldKeyToIdx//老节点key和下标的对象集合letidxInOld//老节点key集合中新节点key的下标letvnodeToMove//idxInOld对应的老节点letrefElm//引用节点}遍历规则:首先将oldStartVnodeoldEndVnode与new结合当oldStartVnode和newStartVnode和newEndVnode或者oldEndVnode和newEndVnode遇到同一个Vnode时,可以直接patchvnode节点如果oldStartVnode和newEndVnode遇到同一个Vnode,则vnode节点要patchVnode,需要把真实节点移到oldEndVnode后面如果oldEndVnode和newStartVnode满足sameVnode,patchVnode的vnode,需要把oldEndVnode对应的dom移到dom前面的oldStartVnode!oldvnode要遍历找到相同的节点,如果有patchVnode,则将elmToMove移动到oldStartIdx对应的dom前面。如果newStartVnode在oldvnode节点中找不到一致的sameVnode,就会调用createElm创建新节点。循环结束时最后,oldStartIdx>oldEndIdx,此时旧vnode节点已经遍历完,新节点还没有,说明新vnode节点多于旧vnode节点。剩下的vnode对应的dom需要插入到真正的dom中,循环结束时,newStartIdx>newEndIdx表示遍历完新的vnode节点,老节点还剩下,需要删除
