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

相关推荐
gnip43 分钟前
链式调用和延迟执行
前端·javascript
SoaringHeart1 小时前
Flutter组件封装:页面点击事件拦截
前端·flutter
杨天天.1 小时前
小程序原生实现音频播放器,下一首上一首切换,拖动进度条等功能
前端·javascript·小程序·音视频
Dragon Wu1 小时前
React state在setInterval里未获取最新值的问题
前端·javascript·react.js·前端框架
Jinuss1 小时前
Vue3源码reactivity响应式篇之watch实现
前端·vue3
YU大宗师1 小时前
React面试题
前端·javascript·react.js
木兮xg1 小时前
react基础篇
前端·react.js·前端框架
ssshooter2 小时前
你知道怎么用 pnpm 临时给某个库打补丁吗?
前端·面试·npm
IT利刃出鞘2 小时前
HTML--最简的二级菜单页面
前端·html
yume_sibai2 小时前
HTML HTML基础(4)
前端·html