v-for时为什么要写 key ?

前言

官方文档中写到 「建议尽可能在使用 v-for 时提供 key attribute,除非遍历输出的 DOM 内容非常简单,或者是刻意依赖默认行为以获取性能上的提升。」

开发vue的朋友也都知道,一定要去写上key 不然通过vue-cli创建的项目会报出警告。也知道key是用来标记当前元素。这篇文章的目的是看一下源码,了解下我们为啥要去写这个key

虚拟dom回忆

之前写过关于虚拟dom执行的流程。我们了解到

  1. 如果oldVnode不是dom元素,并且和vnodesameVnode会进行patchVnode
  2. pathVnode中,如果新老节点都有子节点并且不相等,会执行updateChildren对比子节点,把子节点的差异更新到真实dom
  3. sameVnode中会对比两个虚拟dom的keytag

从代码体会key的作用

下面的代码是没有加key的情况,在页面遍历展示arr数组,当点击按钮的时候往数组插入’X’。

<div id="app">
<button @click="handler">按钮</button>
<ul>
<li v-for="value in arr">{{value}}</li>
</ul>
</div>
<script src="../../dist/vue.js"></script> <script>
  const vm = new Vue({
    el: '#app',
    data: {
      arr: ['a', 'b', 'c', 'd']
    },
    methods: {
      handler () {
		this.arr.splice(1,0,'X')
	 }
   } 
 })
</script>

下面点击按钮后会经历些什么

  1. 点击按钮后,oldVnode是: ul, vnodeul, 符合sameVnode。进行patchVnode
  2. oldVnode的子节点遍历内容是:['a', 'b', 'c', 'd'], vnode的子节点遍历内容是:['a', 'X' ,'b', 'c', 'd'], 新老节点都有子节点并且不相等。进入updateChildren对比子节点
  3. updateChildren 是diff算法的核心,我在文章的最后写了他的对比过程

总结

  • 当没有设置 key 的时候
    updateChildren 中比较子节点的时候,会做三次更新 DOM 操作和一次插入 DOM 的操作
  • 当设置 key 的时候
    updateChildren 中比较子节点的时候,因为 oldVnode 的子节点的 b,c,dnewVnodex,b,ckey 相同,所以只做比较,没有更新 DOM 的操作,当遍历完毕后,会再把 x 插入到 DOM 上DOM 操 作只有一次插入操作。

—————————————————————————————————华丽的分割线

updatechildren的对比过程

  • 对比开始和结束节点的时候分四种

  • 旧开始/新开始

    如果旧开始和新开始是sameVnode(key和sel相同)

    • 调用patchVnode()对比和更新节点
    • 把旧开始和新开始索引往后移动 oldStartIdx++ / oldEndIdx++
      如果不是sameVnode, 开始 旧结束/新结束比较
  • 旧结束/新结束

    如果旧结束和新结束是sameVnode(key和sel相同)

    • 调用patchVnode()对比和更新节点
    • 把旧开始和新开始索引往后移动 oldStartIdx-- / oldEndIdx–
      如果不是, 开始 旧开始/新结束比较
  • 旧开始/新结束

    如果旧开始和新结束是sameVnode(key和sel相同)

    • 调用patchVnode()对比和更新节点

    • 把oldStartVnode 对应的dom元素,移动到右边,更新索引

      如果不是, 开始 旧结束/新开始比较

  • 旧结束/新开始

    如果是sameVnode

    • 调用patchVnode()对比和更新节点
    • 把oldEndVnode 对应的dom元素,移动到左边,更新索引
  • 如果以上四种都不满足

  1. 首先遍历新开始节点,在旧节点数组中依次查找是否有相同key值的节点,
  2. 如果没有,创建新dom元素插入到最前面的位置
  3. 如果找到了,并且判断sel属性是否相同,如果相同,旧节点会被复制给elmToMove,然后调用patchVNode对比节点,然后把elmToMove对应的dom节点移动到最前面
    ——————循环结束后
  • 当老节点的所有子节点先遍历完成,说明新节点有剩余,把剩余节点批量插入到右边
  • 新节点的所有子节点先遍历完成,说明老节点有剩余,把剩余老节点删除
  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值