Vue 3 Diff 算法受 `v-for` 循环中的 `key` 属性影响

Vue 3 的 Diff 算法会受到 v-for 循环中的 key 属性的影响key 的选择直接关系到 Diff 算法的效率和最终的 DOM 更新结果。


key 的作用

在 Vue 中,key 是一种标识,它用于唯一标记每个虚拟 DOM 节点。Diff 算法会根据 key 判断新旧节点是否是同一个节点。

没有 key
  • 如果没有 key,Vue 会默认使用节点的索引值作为标识。
  • 当列表发生变化时,由于索引可能对应了错误的节点,会导致无法正确复用现有节点,从而增加不必要的 DOM 操作。
key
  • 当指定了唯一的 key,Vue 可以准确匹配新旧节点。
  • 这样可以复用相同 key 的节点,避免误匹配或冗余操作,提高性能。

Diff 算法如何处理 key

  1. key 匹配规则

    • 在 Diff 过程中,如果新旧节点的 key 相同,Vue 会认为它们是相同的节点,执行更新操作而非重建。
    • 如果 key 不同,Vue 会移除旧节点并创建新节点。
  2. key 的场景

    • Vue 通过 key 快速定位新旧节点在列表中的位置,避免盲目比较,从而优化性能。
    • Vue 3 使用 最长递增子序列(LIS) 算法来减少 DOM 操作,key 的稳定性是实现该优化的重要前提。
  3. 没有 key 的场景

    • Vue 会退化为按索引逐一比较的模式。
    • 如果数据顺序发生变化,可能会导致大量的节点重建和移动。

案例分析

示例 1:没有 key 的情况
vue 复制代码
<template>
  <div>
    <div v-for="item in items">{{ item }}</div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      items: [1, 2, 3]
    };
  },
  mounted() {
    setTimeout(() => {
      this.items = [3, 2, 1]; // 改变顺序
    }, 1000);
  }
};
</script>

结果

  • Vue 会将 [1, 2, 3][3, 2, 1] 按索引逐个对比。
  • 由于没有 key,节点内容不同,导致所有节点被替换,性能较差。
示例 2:使用唯一的 key
vue 复制代码
<template>
  <div>
    <div v-for="item in items" :key="item">{{ item }}</div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      items: [1, 2, 3]
    };
  },
  mounted() {
    setTimeout(() => {
      this.items = [3, 2, 1]; // 改变顺序
    }, 1000);
  }
};
</script>

结果

  • Vue 根据 key 匹配节点 [1, 2, 3][3, 2, 1],只移动节点位置,而不会销毁重建。
  • 这样可以显著减少 DOM 操作,提高性能。

key 影响 Diff 的性能与结果

  1. 性能影响

    • 没有 key:Vue 需要逐个比对每个节点的内容,无法充分复用节点。
    • key:Vue 可以快速确定哪些节点需要更新、移动或删除,从而减少不必要的 DOM 操作。
  2. 更新结果的准确性

    • 没有 key:可能会导致节点错误复用,渲染结果不符合预期。
    • key:可以确保每个节点的复用和更新都是正确的。
html 复制代码
<template>
  <div class="ForKeyInDiffVNode">
    <el-button @click="change">改变值</el-button>
    <div class="item-class" v-for="(item, idx) in lists" :key="idx">
      <el-checkbox></el-checkbox>
      <span>{{ item.name }}</span>
    </div>
  </div>
</template>

<script setup lang="ts">
import {ref} from "vue";

const lists = ref([
  {
    name: 'hmk',
    age: 20,
    show: false
  },
  {
    name: '张三',
    age: 21,
    show: false
  },
  {
    name: '李四',
    age: 22,
    show: false
  },
  {
    name: '王五',
    age: 23,
    show: false
  }
])
// 之前选中第三个,点击删除后,会出现错乱情况
// 给key加上一个唯一的id即可解决问题
const change = () => {
  lists.value.splice(1, 1)
}

</script>

最佳实践:key 的选择

  1. 使用唯一标识符

    • 使用数据中的唯一标识(如 ID)作为 key

      vue 复制代码
      <div v-for="item in items" :key="item.id">{{ item.name }}</div>
    • 避免使用非唯一的值(如索引)作为 key,因为数据顺序变化时可能会导致错误复用。

  2. 避免使用索引作为 key

    • 索引是动态的,当列表顺序改变或元素插入时,索引会变化,导致 Diff 结果不准确:

      vue 复制代码
      <div v-for="item in items" :key="index">{{ item }}</div>
    • 如果数据内容变化频繁,索引 key 会导致不必要的 DOM 重建。

  3. 保证 key 的稳定性

    • key 应该在组件的整个生命周期内保持不变,否则会导致 Diff 结果错误。

总结

  1. key 对 Diff 算法至关重要

    • 没有 key:Vue 会按索引匹配,导致错误复用和性能下降。
    • key:Vue 可高效、准确地更新节点,减少 DOM 操作。
  2. 选择唯一且稳定的 key

    • 推荐使用数据中的唯一标识符(如 id)。
    • 避免使用索引或动态值作为 key
  3. Diff 性能优化

    • Vue 3 使用 key 时可以充分利用最长递增子序列(LIS)优化算法,最小化 DOM 移动和更新操作,提高性能。
相关推荐
小刘今天学前端了吗20 分钟前
ant-design-vue 1.X 通过id获取a-input组件失败
前端·javascript·vue.js
明月看潮生31 分钟前
青少年编程与数学 02-006 前端开发框架VUE 18课题、逻辑复用
前端·javascript·vue.js·青少年编程·编程与数学
明月看潮生37 分钟前
青少年编程与数学 02-006 前端开发框架VUE 15课题、模板引用
前端·javascript·vue.js·青少年编程·编程与数学
zhangyao9403301 小时前
lodash-实用库及常用方法
前端·javascript·vue.js
计算机-秋大田2 小时前
基于Spring Boot的扶贫助农系统设计与实现(LW+页码+讲解)
java·vue.js·spring boot·后端·课程设计
nyf_unknown4 小时前
(vue)el-table-column type=“selection“表格选框怎么根据条件添加禁选
前端·javascript·vue.js
yqcoder4 小时前
el-tabe 配合 xlsx 导出 excel 文件
javascript·vue.js·elementui
倔强青铜三4 小时前
超好用❤️‍🔥!Chrome新增滚动快照事件,解锁滚动新玩法🚀
前端·javascript·vue.js
会发光的猪。5 小时前
uniapp路由跳转+二级页面详情跳转保留当前页方法教程
前端·javascript·vue.js·uni-app