Vue中`v-for`的key选择:避免陷阱,优化性能

在日常vue项目中,我们在使用v-for时,总是习惯使用index来充当key的,这样操作虽然是很方便的,但是实际上这是存在着一定的风险的。下面我将为大家介绍一下为什么使用index充当key是不安全的。

index为key时所指向的内容是会改变的

当列表的顺序发生变化时,使用index作为 key 可能导致原先所指向的内容会发生改变。让我们通过一个示例来说明这一点。

示例

vue 复制代码
<template>
  <div>
    <button @click="shuffleItems">change</button>
    <ul>
      <li v-for="(item, index) in items" :key="index">
        {{ item.text }}
      </li>
    </ul>
  </div>
</template>

<script>
import { ref } from 'vue';

export default {
  setup() {
    const items = ref([
      { id: 1, text: 'A' },
      { id: 2, text: 'B' },
      { id: 3, text: 'C' }
    ]);

    const shuffleItems = () => {
      items.value.reverse(); 
    };

    return {
      items,
      shuffleItems
    };
  }
};
</script>

在这个示例中,我们使用 reverse 方法来简单地改变列表项的顺序。这会导致原始列表中的元素以相反的顺序重新渲染,但由于我们使用了索引作为 key,Vue 并不知道列表项的顺序变化,因此不会进行相应的移动操作,而是重新创建和销毁元素,这可能导致不必要的性能开销,并且可能会导致意外的结果,特别是在涉及动画效果时。

当点击click后可以看出,下标所表示的值就已经发生了变动。如果仍然使用下标作为key的话,就会产生点击后的效果------值会发生改变,Diff无法辨别新的VDOM和旧的VDOM了。因为 Vue 会根据 key 来确定每个节点的身份。如果改变了列表的顺序,但是索引作为 key 的话,Vue 会错误地认为是相同的节点位置变化了,就会导致以上问题。

解决方案

那么这样的话我们可以通过什么办法去解决这个问题呢。既然它需要的key是不能更改的是唯一的,那么我们可以通过提供一个唯一且稳定的标识符作为 key 来解决,这样 Vue 就能正确地识别节点,并更新 DOM。

示例

vue 复制代码
<template>
  <div>
    <button @click="shuffleItems">click</button>
    <ul>
      <li v-for="item in shuffledItems" :key="item.id">{{ item.name }}</li>
    </ul>
  </div>
</template>

<script setup>
import { ref } from 'vue';

const items = ref([
  { id: 1, name: 'A' },
  { id: 2, name: 'B' },
  { id: 3, name: 'C' }
]);
console.log('items', items.value);
const shuffledItems = ref([...items.value]);

const shuffleItems = () => {

  shuffledItems.value = shuffledItems.value.sort(() => Math.random() - 0.5);
  console.log('shuffledItems', shuffledItems.value);
};
</script>

在这个示例中,我们有一个包含三个对象的 items 数组,每个对象都有一个唯一的 id 属性。当点击按钮时,shuffleItems 方法会打乱 shuffledItems 数组的顺序。由于我们使用对象的 id 属性作为 key,Vue 可以正确地识别每个项,并且在顺序发生变化时能够正确地更新 DOM。这样做的话,即使列表的顺序发生变化,Vue 也能够正确地跟踪每个节点的标识,并且能够高效地更新 DOM,提高了性能和用户体验。

结语

我们要知道Vue 使用一种称为虚拟 DOM 的技术来管理 DOM 更新。在进行比较时,Vue 会将新旧列表的节点按照 key 进行匹配,然后根据匹配的结果确定是更新现有 DOM 元素、删除不需要的元素,还是添加新的元素。可以去看我的这篇文章解析Vue中的虚拟DOM与Diff算法:提升性能的利器 - 掘金 (juejin.cn)

总结

vue使用diff算法,需要使用key来做同级比对。如果使用数组下标作为key,有以下情况:

  • 在数组头部或中部插入或删除元素: 所有key对应的节点的值发生更改,进行重新渲染。造成性能损耗
  • 而如果使用数组中唯一值来作为key:不管是在何处插入或删除节点,其他key对应的节点的值未发生更改,只需插入或删除操作的数组节点。

综上所述,使用 index 作为 key 的风险在于,当列表发生变化时,节点的顺序改变会导致 index 所指向的内容也会发生改变,从而可能导致 Vue 无法正确识别节点的变化,进而影响 DOM 的更新。这种情况下,Vue 可能会错误地创建、销毁或更新 DOM 元素,导致不必要的性能开销和意外的结果。因此,建议在使用 v-for 渲染列表时,尽量避免使用 index 作为 key,而是使用唯一且稳定的标识符作为 key,以确保 Vue 能够正确地识别节点,并更新 DOM。

最后假如您也和我一样,在准备春招。欢迎加微信shunwuyu ,这里有几十位一心去大厂的友友可以相互鼓励,分享信息,模拟面试,共读源码,齐刷算法,手撕面经。来吧,友友们!

相关推荐
万叶学编程2 小时前
Day02-JavaScript-Vue
前端·javascript·vue.js
前端李易安4 小时前
Web常见的攻击方式及防御方法
前端
PythonFun4 小时前
Python技巧:如何避免数据输入类型错误
前端·python
知否技术4 小时前
为什么nodejs成为后端开发者的新宠?
前端·后端·node.js
hakesashou4 小时前
python交互式命令时如何清除
java·前端·python
天涯学馆4 小时前
Next.js与NextAuth:身份验证实践
前端·javascript·next.js
HEX9CF5 小时前
【CTF Web】Pikachu xss之href输出 Writeup(GET请求+反射型XSS+javascript:伪协议绕过)
开发语言·前端·javascript·安全·网络安全·ecmascript·xss
ConardLi5 小时前
Chrome:新的滚动捕捉事件助你实现更丝滑的动画效果!
前端·javascript·浏览器
ConardLi5 小时前
安全赋值运算符,新的 JavaScript 提案让你告别 trycatch !
前端·javascript