为什么不用 index 做 key?

“在 Vue 中,我们在使用 v-for 渲染列表的时候,为什么要绑定一个 key?能不能用 indexkey?”

在聊这个问题之前我们还得需要知道 Vue 是如何操作 DOM 结构的。

虚拟DOM

我们知道,Vue 不可以直接操作 DOM 结构,而是通过数据驱动、指令等机制来间接操作 DOM 结构。当我们修改模版中的数据时,Vue 会触发重新渲染过程,调用render函数,它会返回一个 虚拟 DOM 树,它描述了整个组件模版的结构。

什么是虚拟DOM呢?虚拟DOM是一个对象,没想到吧...我们来看看Vue是如何将template模板里面的东西交给浏览器来渲染的

举个栗子🌰:

<template><ul class="list"><li v-for="item in list" :key="item.index" class="item">{{ item }}</li></ul>
</template><script setup>
import { ref } from 'vue';
const list = ref(['html', 'css', 'js'])
</script>

Vue 在渲染这个列表时,就会调用render函数,它会返回一个类似下面这个虚拟 DOM 树。

let VDom = {tagName: 'ul',props: {class: 'list'},chilren: [{tagName: 'li',props: {class: 'item'},chilren: ['html']},{tagName: 'li',props: {class: 'item'},chilren: ['css']},{tagName: 'li',props: {class: 'item'},chilren: ['js']}]
}

虚拟 DOM 的每个节点对应于真实 DOM 树中的一个节点。

当我们修改数据时,Vue 又会触发重新渲染的过程。

const list = ref(['html', 'css', 'vue']) //修改列表第三项'js'->'vue'

Vue 又会生成一个新的虚拟DOM树:

let VDom = {tagName: 'ul',props: {class: 'list'},chilren: [{tagName: 'li',props: {class: 'item'},chilren: ['html']},{tagName: 'li',props: {class: 'item'},chilren: ['css']},{tagName: 'li',props: {class: 'item'},chilren: ['vue']}]
}

注意观察,这里最后一个节点的子节点为'vue',发生了数据变化,Vue内部又会返回一个新的虚拟 DOM。那么 Vue 是如何将这个变化响应给页面的呢?

摆在面前的有两条路

要么重新渲染这个新的虚拟 DOM ,要么只新旧虚拟 DOM 之间改变的地方。

显而易见,只渲染修改了的地方是不是会更节省性能。

巧了,尤雨溪也是这样想的,于是便有了“ Diff 算法 ”。

Diff 算法

Vue 将新生成的新虚拟 DOM 与上一次渲染时生成的旧虚拟 DOM 进行比较,对比出是哪个虚拟节点更改了,找出这个虚拟节点,并只更新这个虚拟节点所对应的真实节点,而不用更新其他数据没发生改变的节点。

我自己总结了一下Diff算法的过程,由于代码过多,就不在此展示了:

  1. 新旧虚拟DOM对比的时候,Diff 算法比较只会在同层级进行,不会跨层级比较。
  2. 首先比较两个节点的类型,如果类型不同,则废弃旧节点并用新节点替代。
  3. 对于相同类型的节点,进一步比较它们的属性。记录属性差异,以便生成相应的补丁。
  4. 如果两个节点相同,继续递归比较它们的子节点,直到遍历完整个树。
  5. 如果节点有唯一标识,可以通过这些标识来快速定位相同标识的节点。
  6. 如果节点的相同,只是顺序变化,不会执行不必要的操作。

面试官:为什么不用 index 做 key?

平常v-for循环渲染的时候,为什么不建议用 index 作为循环项的 key 呢?

举个栗子🌰:

<div id="app"><ul><li v-for="item in list" :key="item.index">{{item}}</li></ul><button @click="add">添加</button>
</div>
<script>const { createApp, ref } = VuecreateApp({setup() {const list = ref(['html', 'css', 'js']);const add=()=> {list.value.unshift('阳阳羊');}return {list,add}}}).mount('#app')
</script>

我们发现添加操作导致的整个列表的重新渲染,按道理来说,Diff 算法会复用后面的三项,因为它们只是位置发生了变化,内容并没有改变。但是我们回过头来发现,我们在前面添加了一项,导致后面三项的 index 变化,从而导致 key 值发生变化。Diff 算法失效了?

那我们可以怎么解决呢?其实我们只要使用一个独一无二的值来当做key就行了

<div id="app"><ul><li v-for="item in list" :key="item.id">{{item.name}}</li></ul><button @click="add">添加</button>
</div>
<script>const { createApp, ref } = VuecreateApp({setup() {const list = ref([{ name: "html", id: 1 }, { name: "css", id: 2 }, { name: "js", id: 3 }, ]);const add=()=> {list.value.unshift({ name: '阳阳羊', id: 4 });}return {list,add}}}).mount('#app')
</script>

这样,key就是永远不变的,更新前后都是一样的,并且又由于节点的内容本来就没变,所以 Diff 算法完美生效,只需将新节点添加到真实 DOM 就行了。

如果后端没有返回 唯一 id ,唯一标识都可以的

最后

看到这里,希望你已经对Diff 算法有了初步的了解,想要深入了解,可以自行查看Diff 源码。总的来说,Diff 算法是一项关键的技术,为构建响应式和高效的用户界面提供了基础。最后,祝你面试顺利,学习进步!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/521275.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

js小案例-表格隔行变色

运行效果: 代码演示: <!DOCTYPE html> <html><head><meta charset="utf-8" /><title>18-案例-表格隔行变色</title></head><body><table id="tab1" border="1" width="800" al…

北斗卫星引领智能油气管线革新

北斗卫星引领智能油气管线革新 现代化的油气管线系统已成为国家经济发展的重要基础设施&#xff0c;而北斗卫星则为这些管线注入了新的活力。北斗卫星作为中国自主研发的卫星导航定位系统&#xff0c;其准确度和稳定性在全球范围内享有盛誉。在智能化时代的背景下&#xff0c;…

【OpenGL实践02】glDrawElements的使用案例

目录 一、说明二、顶点顺序渲染和选择渲染2.1 基本方法函数2.2.顶点数据管理2.3 层级关系 三、测试EBO的代码四、总结五、后记 一、说明 我们常用的着色器绘制函数是glDrawArray和glDrawElements&#xff0c;glDrawArray我们已经使用的很熟练&#xff0c;不需要重提&#xff…

深入了解Python的eval函数:基础用法与潜在危险【第118篇—eval函数】

深入了解Python的eval函数&#xff1a;基础用法与潜在危险 在Python中&#xff0c;eval函数是一个强大而灵活的工具&#xff0c;它允许将字符串作为代码来执行。然而&#xff0c;虽然eval在某些情况下非常方便&#xff0c;但它也潜藏着一些潜在的危险&#xff0c;如果不小心使…

小火星露谷管理器 如何禁用管理器下载?

错误操作 当你在N网点击下载时&#xff0c;你可能会点击左边第一个按钮进行下载&#xff0c;如图&#xff1a; 然后你可能会看到这样的一个提示&#xff1a; 很多用户看着这个提示误以为小火星露谷管理器禁用了N网的下载。 正确操作 N网网页上的按钮MOD MANAGER DOWNLOAD翻…

海外IP代理应用:亚马逊使用什么代理IP?

代理IP作为网络活动的有力工具&#xff0c;同时也是跨境电商的必备神器。亚马逊作为跨境电商的头部平台&#xff0c;吸引了大量的跨境电商玩家入驻&#xff0c;想要做好亚马逊&#xff0c;养号、测评都需要代理IP的帮助。那么应该使用什么代理IP呢&#xff1f;如何使用&#xf…

这6个国产办公软件凭什么被推荐?是真的好用

在数字化时代&#xff0c;办公软件已经成为我们日常生活和工作中不可或缺的工具。近年来&#xff0c;国产办公软件凭借其高效、便捷的特点受到了广泛的好评。今天给大家分享6款备受推荐的国产办公软件&#xff0c;让你了解它们的优势和使用场景。 一、Listary&#xff1a;强大的…

Python数据分析库之pandera使用详解

概要 在数据科学和数据分析中,数据的质量至关重要。不良的数据质量可能导致不准确的分析和决策。为了确保数据的质量,Python Pandera 库应运而生。本文将深入介绍 Python Pandera,这是一个用于数据验证和清洗的库,并提供丰富的示例代码,帮助大家充分利用它来提高数据质量…

学生课程参与度的一些情况

喊口号 日常校园中不乏各式各样的口号和标语。 但是能否落实到实处&#xff0c;非常难以评定的。 以学生为中心 实际上&#xff0c;学生对于课程几乎没有任何选择权和掌控权。 1&#xff0c;课程并非是学生自己选择的&#xff0c;还是培养方案安排的。 2&#xff0c;课程教…

Python与Go代码转换库之grumpy使用详解

概要 在软件开发领域,Python 和 Go 是两种备受欢迎的编程语言,它们各自拥有独特的优势和特点。Python 以其简洁、易学和强大的生态系统而闻名,而 Go 则以其高效、并发和简洁的语法而受到广泛青睐。然而,在某些情况下,开发人员可能会希望将 Python 代码转换为 Go 代码,以…

k8s-生产级的k8s高可用(1) 24

高可用集群 实验至少需要三个master&#xff08;控制节点&#xff09;&#xff0c;一个可以使外部可以访问到master的load balancer&#xff08;负载均衡&#xff09;以及一个或多个外部节点worker&#xff08;也要部署高可用&#xff09;。 再克隆三台主机 清理并重启 配置两…

【node版本问题】运行项目报错 PostCSS received undefined instead of CSS string

最近该项目没有做任何修改&#xff0c;今天运行突然跑不起来报错了 PostCSS received undefined instead of CSS string 【原因】突然想起来期间有换过 node 版本为 16.17.1 【解决】将 node 版本换回之前的 14.18.0 就可以了