這篇文章主要介紹“vue怎么實現diff算法”的相關知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“vue怎么實現diff算法”文章能幫助大家解決問題。
模塊路徑:src\core\vdom\patch.js
問題:diff算法主要是對相同節點的子節點進行的比較和更新,也就是相同級別節點的比較算法。dom對比更新,本著盡量減少dom的銷毀和重建,所以diff算法盡量先移動,后增刪
過程分析
比較舊VNode節點oldCh和新VNode節點newCh,創建四個索引進行比較:
兩個索引oldStartIdx(舊VNode開始位置)和oldEndIdx(舊VNode結束位置)分別指向oldCh的開始和結束下標
兩個索引newStartIdx(新VNode開始位置)和newEndIdx(新VNode開始位置)分別指向newCh的開始和結束下標
并創建四個索引位置對應的VNode節點oldStartVnode,oldEndVnode,newStartVnode,newEndVnode,代碼如下:
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
根據索引遍歷節點 while(oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx):
首先是異常情況判斷,然后根據當前節點情況判斷是否以下四種情況
if (isUndef(oldStartVnode)) {undefined
oldStartVnode = oldCh[++oldStartIdx] // Vnode has been moved left
} else if (isUndef(oldEndVnode)) {undefined
oldEndVnode = oldCh[--oldEndIdx]
}
遍歷判斷1: 如果oldStartIdx的節點和newStartIdx節點相同,去patchVnode判斷節點內部是否相同,oldStartIdx++,newStartIdx++ (patchVnode是詳細比較兩個節點內容的函數)
else if (sameVnode(oldStartVnode, newStartVnode)) {undefined
// 兩個節點相同,直接將該VNode節點進行patchVnode
patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue, newCh, newStartIdx)
oldStartVnode = oldCh[++oldStartIdx]
newStartVnode = newCh[++newStartIdx]
}
遍歷判斷2: 如果oldEndIdx的節點和newEndIdx節點相同,去patchVnode判斷節點內部是否相同,oldEndIdx--,newEndIdx--
else if (sameVnode(oldEndVnode, newEndVnode)) {undefined
// 兩個節點相同,直接將該VNode節點進行patchVnode
patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue, newCh, newEndIdx)
oldEndVnode = oldCh[--oldEndIdx]
newEndVnode = newCh[--newEndIdx]
}
遍歷判斷3: 如果oldStartIdx的節點和newEndIdx節點相同,去patchVnode判斷節點內部是否相同,oldStartIdx++,newEndIdx--,把oldStartIdx對應節點向右移動
else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved right
// 進行patchVnode,把oldStartVnode移到最后
patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue, newCh, newEndIdx)
//canMove移動oldStartVnode節點到oldEndVnode后面
canMove && nodeOps.insertBefore(parentElm, oldStartVnode.elm, nodeOps.nextSibling(oldEndVnode.elm))
oldStartVnode = oldCh[++oldStartIdx]
newEndVnode = newCh[--newEndIdx]
}
遍歷判斷4: 如果oldEndIdx的節點和newStartIdx節點相同,去patchVnode判斷節點內部是否相同,oldEndIdx--,newStartIdx++,把oldEndIdx對應節點向左移動
else if (sameVnode(oldEndVnode, newStartVnode)) { // Vnode moved left
// oldEndVnode 和 newStartVnode相同,進行patchVnode
patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue, newCh, newStartIdx)
//,把oldEndVnode 移動到最前面
canMove && nodeOps.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm)
oldEndVnode = oldCh[--oldEndIdx]
newStartVnode = newCh[++newStartIdx]
}
遍歷判斷5: 如果上述四種情況都不存在,則用新VNode當前的newStartVnode去舊VNode里找(盡量不新建dom,找到了就移動,找不到再新建)
如果找到了,則調用patchVnode去比較詳細內容,并且移動這個節點到newStartIdx位置,
如果沒找到,則創建新節點并插入
else {undefined
// 以上四種情況都不滿足
// newStartVnode依次和舊的節點進行比較
// 從新的節點開通取一個,去老節點中查找相同節點
// 先找新開始節點的key和老節點相同的索引,如果沒找到再通過sameVnode找
if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx)
idxInOld = isDef(newStartVnode.key)
? oldKeyToIdx[newStartVnode.key]
: findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx)
// 如果沒找到,創建節點并插入到最前面
if (isUndef(idxInOld)) { // 沒找到 New element
createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx)
} else { //找到了 獲取要移動的老節點
vnodeToMove = oldCh[idxInOld]
if (sameVnode(vnodeToMove, newStartVnode)) {undefined
// 兩節點相同,執行patchVnode
patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue, newCh, newStartIdx)
oldCh[idxInOld] = undefined
//并將找到的舊節點移動到最前面
canMove && nodeOps.insertBefore(parentElm, vnodeToMove.elm, oldStartVnode.elm)
} else {// 兩節點不同,創建新元素
createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx)
}
}
newStartVnode = newCh[++newStartIdx]//移動索引
}
遍歷完成(走出while循環時說明肯定有新/舊節點被遍歷完了):
結束判斷1:當結束時 oldStartIdx > oldEndIdx ,說明舊節點遍歷完了,新節點沒遍歷完,則說明新節點比老節點多,把newStartIdx >到newEndIdx直接的節點插入到老節點后面
結束判斷2:當結束時 newStartIdx > newEndIdx ,說明新節點遍歷完了,舊節點沒遍歷完,則說明老節點比新節點多,把的oldStartIdx到oldEndIdx之間的節點刪除
關于“vue怎么實現diff算法”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識,可以關注億速云行業資訊頻道,小編每天都會為大家更新不同的知識點。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。