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 ,这里有几十位一心去大厂的友友可以相互鼓励,分享信息,模拟面试,共读源码,齐刷算法,手撕面经。来吧,友友们!

相关推荐
谭光志5 分钟前
工具塞满上下文窗口怎么办?深度拆解 AI Agent Tool Search 按需加载实现原理
前端·后端·ai编程
Hilaku7 分钟前
前端架构师的克制:什么时候该坚决对新技术说不?
前端·javascript·程序员
陈随易33 分钟前
前端项目部署只要30秒
前端·后端·程序员
本末倒置18334 分钟前
从 Vue CLI 迁移到 Rsbuild 后,JSX 突然全挂了?一次排查记录
前端
古夕35 分钟前
多应用同域部署下 Favicon 异常问题排查与治理
前端·javascript
YIAN37 分钟前
从零手写文件读取 MCP 服务:一文吃透 Model Context Protocol 全链路通信原理
前端·后端·mcp
Imchendiana37 分钟前
《狂人日记NO.9》— 前后端一把梭,我的全栈实录
前端·后端
逍遥运德44 分钟前
前端工程化-03:包管理NPM-package.json 和 package-lock.json 详解
前端·前端框架·前端工程化
逗逗豆巴吧1 小时前
WPS 加载项中实现当前文档直接上传
前端·javascript·ecmascript 6
慢功夫1 小时前
💡【调试】按下 F5 后,mini-vscode 到底做了什么
前端·visual studio code