简单看看diff算法——vue中的v-for中的key是什么作用?

169 阅读2分钟

首先认识两个概念:

VNode

VNode全称Virtual Node,也就是虚拟节点,无论是组件还是元素,最终在Vue中表现出来的都是一个个VNode,VNode的本质是一个JavaScript对象。VNode最大的好处是做多平台适配,比如服务端渲染,移动端渲染,浏览器渲染。

虚拟DOM

简单来说就是用JS模拟DOM结构,虚拟DOM是一个JS对象。一大堆元素会形成一个VNode Tree,VNode的本质就是用树型结构的JS对象来描述真实的DOM结构的信息,这个树结构的JS对象包含了整个DOM结构的信息。

//真实DOM
<ul id="list"> 
    <li class="item">你好</li> 
    <li class="item">不错</li>
</ul>
//虚拟DOM
let VDOM = {
  tagName:'ul',
  props:{
    id:'list'
  },
  children:[
    {
      tagName:'li',props:{ class:'item'},children:['你好']
    },
    {
      tagName:'li',props:{ class:'item'},children:['不错']
    }
  ]
}

数据改变 -> 虚拟DOM(计算变更)-> 操作真实DOM -> 视图更新

diff算法

上面的例子中,如果只改变其中一个li标签的值,diff算法会查出来这个改变的li标签,来更新DOM。

diff算法通过对比新旧虚拟DOM,找出改变的虚拟节点,然后只更新这个虚拟节点对应的真实节点,更新真实DOM,更加精准,也提高了效率。

v-for中没有key值会怎么办?

根据vue.js源码,没有key值的话:

  1. 会遍历节点数小的,节点值相等的不修改,复用;
  2. 节点值不相等的修改为新节点的值;
  3. 如果旧节点数大于新节点数,删除剩余的旧节点;如果新节点数大于旧节点数,创建新的节点。

image.png

image.png 这种方式性能比较低。

v-for中有key值怎么办?

  1. 从头部开始遍历,节点相同(类型和值都相同),执行patch,继续遍历,节点不同,跳出循环break;
  2. 从尾部开始遍历,节点相同,执行patch,继续遍历,节点不同,跳出循环break;
  3. 如果旧节点遍历完了, 依然有新的节点, 那么新的节点就是添加(mount);
  4. 如果新的节点遍历完了, 还有旧的节点, 那么旧的节点就是移除的;
  5. 如果中间存在不知道如何排序的位置序列,那么就使用key建立索引图,最大限度使用旧节点。

image.png

image.png

image.png

image.png

image.png

所以发现,Vue在进行diff算法的时候,会尽量利用key进行优化操作:

  1. 在没有key的时候效率非常低;
  2. 在进行插入或者重置顺序的时候,保持相同的key可以让diff算法更加高效。

image.png