关于key 上一张讲了vue的patch过程 有一点讲的还是很含糊的 就是key相关的 提到key这概念 在vue1.0中它的前身好像是v-track 作用不用说都知道 是为了优化diff操作 那它到底是如何做到这一点的呢 我们先看看代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 if (isUndef (oldKeyToIdx)) { oldKeyToIdx = createKeyToOldIdx (oldCh, oldStartIdx, oldEndIdx); }idxInOld = isDef (newStartVnode.key ) ? oldKeyToIdx[newStartVnode.key ] : findIdxInOld (newStartVnode, oldCh, oldStartIdx, oldEndIdx); if (isUndef (idxInOld)) { 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 { createElm (newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm , false , newCh, newStartIdx); } } newStartVnode = newCh[++newStartIdx];
这段代码是updateChildren方法里的一个if-branch 上一章节有提到这部分 我就不多赘述了 这段代码是干嘛的呢 先是通过oldVnodeChildren计算出老的子节点对应的key 每次updateChildren只需要计算一次
1 2 3 4 5 6 7 8 9 function createKeyToOldIdx (children, beginIdx, endIdx ) { var i, key; var map = {}; for (i = beginIdx; i <= endIdx; ++i) { key = children[i].key ; if (isDef (key)) { map[key] = i; } } return map }
也就是说如果一个节点有key 就把这个key当做map里的key值 把这个节点所处的index 当做value 比如一群子节点A, B, C, D key分别是’A’, ‘B’, ‘C’, ‘D’ 生成的oldKeyToIdx就是
1 2 3 4 5 6 { A : 0 , B : 1 , C : 2 , D : 3 , }
这样就算这些子节点的顺序变了 但是key没变 很容易能找到它在oldVnodesChildren中对应的节点 所以这才作为性能优化的一种手段
那为什么不推荐使用数组下标作为作为key呢 我们看看用数组下标作为key的oldKeyToIdx
1 2 3 4 5 6 { 0 : 0 , 1 : 1 , 2 : 2 , 3 : 3 , }
这样如果子节点顺序发生了变化 比如变成了BADC 此时B的key由1变成了0 实际上我们在oldVnodesChildren中找到的是A 这时候如果A和B经过sameVnode判断不是同一个节点 会调用createElm创建新节点 那我们岂不是得不偿失啦 这样的key就没有意义了