1. 程式人生 > >vue 虛擬dom實現原理

vue 虛擬dom實現原理

function updateChildren (parentElm, oldCh, newCh, insertedVnodeQueue, removeOnly) {
    // 為oldCh和newCh分別建立索引,為之後遍歷的依據
    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, elmToMove, refElm // 直到oldCh或者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) oldStartVnode = oldCh[++oldStartIdx] newStartVnode =
newCh[++newStartIdx] } else if (sameVnode(oldEndVnode, newEndVnode)) { patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue) oldEndVnode = oldCh[--oldEndIdx] newEndVnode = newCh[--newEndIdx] } else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved right patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue) 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) // 插入到老的開始節點的前面 canMove && nodeOps.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm) oldEndVnode = oldCh[--oldEndIdx] newStartVnode = newCh[++newStartIdx] } else { // 如果以上條件都不滿足,那麼這個時候開始比較key值,首先建立key和index索引的對應關係 if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx) idxInOld = isDef(newStartVnode.key) ? oldKeyToIdx[newStartVnode.key] : null // 如果idxInOld不存在 // 1. newStartVnode上存在這個key,但是oldKeyToIdx中不存在 // 2. newStartVnode上並沒有設定key屬性 if (isUndef(idxInOld)) { // New element // 建立新的dom節點 // 插入到oldStartVnode.elm前面 // 參見createElm方法 createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm) newStartVnode = newCh[++newStartIdx] } else { elmToMove = oldCh[idxInOld] /* istanbul ignore if */ if (process.env.NODE_ENV !== 'production' && !elmToMove) { warn( 'It seems there are duplicate keys that is causing an update error. ' + 'Make sure each v-for item has a unique key.' ) // 將找到的key一致的oldVnode再和newStartVnode進行diff if (sameVnode(elmToMove, newStartVnode)) { patchVnode(elmToMove, newStartVnode, insertedVnodeQueue) oldCh[idxInOld] = undefined // 移動node節點 canMove && nodeOps.insertBefore(parentElm, newStartVnode.elm, oldStartVnode.elm) newStartVnode = newCh[++newStartIdx] } else { // same key but different element. treat as new element // 建立新的dom節點 createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm) newStartVnode = newCh[++newStartIdx] } } } } // 如果最後遍歷的oldStartIdx大於oldEndIdx的話 if (oldStartIdx > oldEndIdx) { // 如果是老的vdom先被遍歷完 refElm = isUndef(newCh[newEndIdx + 1]) ? null : newCh[newEndIdx + 1].elm // 新增newVnode中剩餘的節點到parentElm中 addVnodes(parentElm, refElm, newCh, newStartIdx, newEndIdx, insertedVnodeQueue) } else if (newStartIdx > newEndIdx) { // 如果是新的vdom先被遍歷完,則刪除oldVnode裡面所有的節點 // 刪除剩餘的節點 removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx) } }