vue diff算法全理会(3)

假如新旧VNode都是静态的,同时它们的key沟通(代表同一节点),而且新的VNode是clone可能是标志了once(标志v-once属性,只渲染一次),那么只需要替换elm以及componentInstance即可。

假如vnode.text不是文本节点,新老节点均有children子节点,且新老节点的子节点不沟通的时候,则对子节点举办diff操纵,挪用updateChildren,这个updateChildren也是diff的焦点。

假如老节点没有子节点而新节点存在子节点,先清空老节点DOM的文本内容,然后为当前DOM节点插手子节点

当新节点没有子节点而老节点有子节点的时候,则移除该DOM节点的所有子节点。

当新老节点都无子节点的时候,只是文本的替换。

updateChildren

我们页面的dom是一个树状布局,上面所讲的patchVnode要领,是复用同一个dom元素,而假如新旧两个VNnode工具都有子元素,我们应该怎么去较量复用元素呢?这就是我们updateChildren要领所要做的事儿

/* diff焦点要领,较量优化 */ function updateChildren (parentElm, oldCh, newCh, insertedVnodeQueue, removeOnly) { let oldStartIdx = 0 let newStartIdx = 0 let oldEndIdx = oldCh.length - 1 let oldStartVnode = oldCh[0] let oldEndVnode = oldCh[oldEndIdx] let newEndIdx = newCh.length - 1 let 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 transitions const canMove = !removeOnly if (process.env.NODE_ENV !== 'production') { 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)) { /*前四种环境其实是指定key的时候,鉴定为同一个VNode,则直接patchVnode即可,别离较量oldCh以及newCh的两端节点2*2=4种环境*/ 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 right patchVnode(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 left patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue, newCh, newStartIdx) canMove && nodeOps.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm) oldEndVnode = oldCh[--oldEndIdx] newStartVnode = newCh[++newStartIdx] } else { /* 生成一个key与旧VNode的key对应的哈希表(只有第一次进来undefined的时候会生成,也为后头检测反复的key值做铺垫) 好比childre是这样的 [{xx: xx, key: 'key0'}, {xx: xx, key: 'key1'}, {xx: xx, key: 'key2'}] beginIdx = 0 endIdx = 2 功效生成{key0: 0, key1: 1, key2: 2} */ if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx) /*假如newStartVnode新的VNode节点存在key而且这个key在oldVnode中能找到则返回这个节点的idxInOld(即第几个节点,下标)*/ idxInOld = isDef(newStartVnode.key) ? oldKeyToIdx[newStartVnode.key] : findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx) if (isUndef(idxInOld)) { // New element /*newStartVnode没有key可能是该key没有在老节点中找到则建设一个新的节点*/ createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx) } else { /*获取同key的老节点*/ vnodeToMove = oldCh[idxInOld] if (sameVnode(vnodeToMove, newStartVnode)) { /*假如新VNode与获得的有沟通key的节点是同一个VNode则举办patchVnode*/ patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue, newCh, newStartIdx) /*因为已经patchVnode进去了,所以将这个老节点赋值undefined,之后假如尚有新节点与该节点key沟通可以检测出来提示已有反复的key*/ oldCh[idxInOld] = undefined /*当有标识位canMove实可以直接插入oldStartVnode对应的真实Dom节点前面*/ canMove && nodeOps.insertBefore(parentElm, vnodeToMove.elm, oldStartVnode.elm) } else { // same key but different element. treat as new element /*当新的VNode与找到的同样key的VNode不是sameVNode的时候(好比说tag纷歧样可能是有纷歧样type的input标签),建设一个新的节点*/ createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx) } } newStartVnode = newCh[++newStartIdx] } } if (oldStartIdx > oldEndIdx) { /*全部较量完成今后,发明oldStartIdx > oldEndIdx的话,说明老节点已经遍历完了,新节点比老节点多,所以这时候多出来的新节点需要一个一个建设出来插手到真实Dom中*/ refElm = isUndef(newCh[newEndIdx + 1]) ? null : newCh[newEndIdx + 1].elm addVnodes(parentElm, refElm, newCh, newStartIdx, newEndIdx, insertedVnodeQueue) } else if (newStartIdx > newEndIdx) { /*假如全部较量完成今后发明newStartIdx > newEndIdx,则说明新节点已经遍历完了,老节点多余新节点,这个时候需要将多余的老节点从真实Dom中移除*/ removeVnodes(oldCh, oldStartIdx, oldEndIdx) } }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wsjfdz.html