Vue2+采用diff算法來進(jìn)行新舊vnode的對比從而更新DOM節(jié)點(diǎn)。而通常在我們使用v-for這個指令的時候,Vue會要求你給循環(huán)列表的每一項添加唯一的key,那么這個key在渲染列表時究竟起到了什么作用呢?
在解釋這一點(diǎn)之前,你最好已經(jīng)了解Vue的diff算法的具體原理是什么。
Vue2更新真實(shí)DOM的操作主要是兩種:創(chuàng)建新DOM節(jié)點(diǎn)并移除舊DOM節(jié)點(diǎn)和更新已存在的DOM節(jié)點(diǎn),這兩種方式里創(chuàng)建新DOM節(jié)點(diǎn)的開銷肯定是遠(yuǎn)大于更新或移動已有的DOM節(jié)點(diǎn),所以在diff中邏輯都是為了減少新的創(chuàng)建而更多的去復(fù)用已有DOM節(jié)點(diǎn)來完成DOM的更新。
在新舊vnode的diff過程中,key是判斷兩個節(jié)點(diǎn)是否為同一節(jié)點(diǎn)的首要條件:
// 參見Vue2源碼 core/vdom/patch.jsfunction sameVnode (a, b) { return ( a.key === b.key && ( ( a.tag === b.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的key值都未定義的話那么兩個key都為undefined,a.key === b.key 是成立的
接下來是在updateChildren方法中,這個方法會對新舊vnode進(jìn)行diff,然后將比對出的結(jié)果用來更新真實(shí)的DOM
// 參見Vue2源碼 core/vdom/patch.jsfunction updateChildren (parentElm, oldCh, newCh, insertedVnodeQueue, removeOnly) { ... while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) { if (isUndef(oldStartVnode)) { ... } else if (isUndef(oldEndVnode)) { ... } else if (sameVnode(oldStartVnode, newStartVnode)) { ... } else if (sameVnode(oldEndVnode, newEndVnode)) { ... } else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved right ... } else if (sameVnode(oldEndVnode, newStartVnode)) { // Vnode moved left ... } 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 element createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx) } else { vnodeToMove = oldCh[idxInOld] if (sameVnode(vnodeToMove, newStartVnode)) { patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue, newCh, newStartIdx) oldCh[idxInOld] = undefined canMove && nodeOps.insertBefore(parentElm, vnodeToMove.elm, oldStartVnode.elm) } else { // same key but different element. treat as new element createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx) } } newStartVnode = newCh[++newStartIdx] } } ...}
新聞熱點(diǎn)
疑難解答
圖片精選