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
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)}}
export functionrenderStatic(index: number,isInFor: boolean): VNode|Array<VNode>{const cached =this._staticTrees||(this._staticTrees=[])lettree=cached[index]// if has already-rendered static tree and not inside v-for,// we can reuse the same tree.if(tree&&!isInFor){returntree}// otherwise, render a fresh tree.tree=cached[index]=this.$options.staticRenderFns[index].call(this._renderProxy,null,this// for render fns generated for functional component templates)markStatic(tree,`__static__${index}`,false)returntree}/** * Runtime helper for v-once. * Effectively it means marking the node as static with a unique key. */
export functionmarkOnce(tree: VNode|Array<VNode>,index: number,key: string){markStatic(tree,`__once__${index}${key ? `_${key}` : ``}`,true)returntree}functionmarkStatic(tree: VNode|Array<VNode>,key: string,isOnce: boolean){if(Array.isArray(tree)){for(leti=0;i<tree.length;i++){if(tree[i]&&typeoftree[i]!=='string'){markStaticNode(tree[i],`${key}_${i}`,isOnce)}}}else{markStaticNode(tree,key,isOnce)}}functionmarkStaticNode(node,key,isOnce){node.isStatic=truenode.key=keynode.isOnce=isOnce}
vdom diff
用虚拟dom的框架都会需要diff算法来patch新旧节点,vue采用的是双端(both end)算法,这个算法基于Snabbdom,下面是vue对于子节点的diff的算法
我们会看到把新旧vodm丢进去对比子节点的时候有四个下标,oldStartIdx,oldEndIdx,newStartIdx,newEndIdx。当循环新旧子节点的长度,比较的时候,会进行不同下标位置的判断,判断到sameVnode时可能会更新节点。比较顺序如图上,双端比较法的好处在于如果按照理想的场景对比,只需要移动一次节点就足够了,只需要将我们的node-3移动到前面。
如果这四种对比都没找到的话,newStartVnode,也就是我们判断里第一个新节点存在key的话,将会遍历一遍找旧节点有相同的key,如果没有的话,说明是个新节点,需要创建再插入,如果存在,说明被插入到中间了,那就找到这个位置,插入进去,并把相同key的旧节点设置为undefined,表示它已经被移动过了,不需要再进行diff,这样就能在一开头里判断
isUndef(oldStartVnode)
跳过了sameVnode的判断是相同的key,或者tag,一样的sameInputType等等。所以如果遇到元素是被插入在第一个子节点的场景的时候,当没有key时,可能会导致所有的节点都需要更新,又要更新hook,attr,class,style,context巴拉巴拉一大堆会浪费很多开销,这时候就得用到key来保证更高效的diff
patchVnode会更新节点包括同步更新子节点并且触发更新hook。
不过在vue的patchVode里会看到这一小段
如果节点是静态的那么将会跳过。这是用过于判断是不是一次性渲染来跳过diff
我们看到当节点为v-once,将会被标记成isStatic=true ,这样将会跳过diff。那么说其实静态的模板节点可以这样优化?是的,vue3也引用了这种思维将其用在普通的静态的节点实现diff的优化。
现在都已经2021年了,不会吧不会吧,vue3都出来了还有人写vue2 vdom diff。其实写东西只是梳理加深印象的过程,也为了引用到下次的vue3。回到传统的diff算法,其实不太高效,因为就算是静态的节点也会比较,会可能造成很多没必要的开销。当然在vue里也做了很多的优化,比如vue3在编译分析模板的时候可以将静态节点标记,进一步的优化呢还可以进行静态变量提升,不用每次render的时候重新创建新的静态节点,将会减少内存占用丶或者分析节点上哪些属性是动态的哪些是静态的属性打上patchFlag来告诉runtime可以跳过某些diff丶catchHandlers把事件侦听器缓存起来,防止每次重新渲染的时候创建新的函数等等。所以vdom也别太神话,用不好还会增加开销,不如我innerhtml一把梭。当然它的概念还是不错的,将元素和渲染过程抽象化出来,达到更好的跨平台
没了,下次在写
The text was updated successfully, but these errors were encountered: