结合源码,谈谈vue中key的作用和工作原理

对应源码的位置src\core\vdom\patch.js-updateChildren()

1.测试的案例代码


  

{{item}}

2.案例图解

image.png

image.png

五次更新,一次追加
而加了key的执行过程是这样的


image.png

尝试更新5次(但是并没有做操作),1次追加。

这样的话,严谨来说不加key更新了三次,追加了一次,用了key只追加了一次。

3.源码分析(执行测试代码进行调试)

头尾比较4中情况 :新节点从头开始和旧节点从头开始,新头和旧尾,旧头和新尾,旧头和旧尾

3.1 源码

//循环条件,开始索引不能大于结束索引
while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
//头尾指针调整
      if (isUndef(oldStartVnode)) {
        oldStartVnode = oldCh[++oldStartIdx] // Vnode has been moved left
      } else if (isUndef(oldEndVnode)) {
        oldEndVnode = oldCh[--oldEndIdx]
//后面是头尾比较4中情况
      } else if (sameVnode(oldStartVnode, newStartVnode)) {
        patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue, newCh, newStartIdx)
       ...
      } else if (sameVnode(oldEndVnode, newEndVnode)) {
//两个开头相同
        patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue, newCh, newEndIdx)
//索引向后移动一位
       oldStartVnode = oldCh[++oldStartIdx]
        newStartVnode = newCh[++newStartIdx]
      } else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved right
        patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue, newCh, newEndIdx)
     ...
      } else if (sameVnode(oldEndVnode, newStartVnode)) { // Vnode moved left
        patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue, newCh, newStartIdx)
       ...
      } else {
        ...
    }
//整理工作:必定有数组还有剩下的元素未处理
    if (oldStartIdx > oldEndIdx) {
//老的结束了,这种情况说明新的数组里还有剩下的节点
      refElm = isUndef(newCh[newEndIdx + 1]) ? null : newCh[newEndIdx + 1].elm
      addVnodes(parentElm, refElm, newCh, newStartIdx, newEndIdx, insertedVnodeQueue)
    } else if (newStartIdx > newEndIdx) {
//新的结束了,此时删除老数组中剩下的即可
      removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx)
    }

3.2 不加key

如果不加key,每一次都会判断为相同节点,那么就会进入patchVnode方法,强行进行更新。
判断相同节点的方法是sameVnode

function sameVnode (a, b) {
  return (
    a.key === b.key && (
      (
        a.tag === b.tag &&//标签名
        a.isComment === b.isComment &&//是否为注释
        isDef(a.data) === isDef(b.data) &&//data有没有发生变化
        sameInputType(a, b)//是不是input类型
      ) || (
        isTrue(a.isAsyncPlaceholder) &&
        a.asyncFactory === b.asyncFactory &&
        isUndef(b.asyncFactory.error)
      )
    )
  )
}

3.3 加了key

加key的时候,如果新旧节点不是相同节点就不会进入patchVnode方法

4.总结

结论

    1. key的作用主要是为了高效的更新虚拟DOM,其原理是vue在patch过程中通过key可以精准判断两个节点是否是同一个,从而避免频繁更新不同元素,使得整个patch过程更加高效,减少DOM操作量,提高性能。
  • 2.另外,若不设置key还可能在列表更新时引发一些隐蔽的bug
    1. vue中在使用相同标签名元素的过渡切换时,也会使用到key属性,其目的也是为了让vue可以区分它们,否则vue只会替换其内部属性而不会触发过渡效果。
  • 4.带上原理解释一遍:
  • 源码在patch的过程中,会执行patchVnode,patchVnode过程中会执行updateChildren这个方法,他会更新所有的两个新旧的子元素,那么在这个过程中,通过key就可以精准的判断,当前在循环的这两个节点是不是一个节点。

  • 如果没有加key的话,永远会认为是相同的节点,所以能做的操作只有强硬的去更新,这样的话在这个过程,就没有办法避免频繁的更新过程,需要额外做很多dom操作。不加key的性能就会很差

  • 如果加上key,就可以通过一系列内部优化算法,比如说猜测收尾结构的相似性,由于大部分情况下,元素不会发生大量的位置变化,所以会很高效的结束循环,减少大量的更新过程。

你可能感兴趣的:(结合源码,谈谈vue中key的作用和工作原理)