使用v-for时为什么不能用index作为key值

1、Vue渲染数据的策略

Vue 默认按照“就地更新”的策略来更新通过 v-for 渲染的元素列表。当数据项的顺序改变时,Vue 不会随之移动 DOM 元素的顺序,而是就地更新每个元素,确保它们在原本指定的索引位置上渲染。

默认模式是高效的,但只适用于列表渲染输出的结果不依赖子组件状态或者临时 DOM 状态 (例如表单输入值) 的情况

2、虚拟DMO的 diff 算法

diff算法采用同级比较。

        1、tag 标签不一致直接新节点替换旧节点。

        2、tag 标签一样。

                先替换属性

                对比子元素

                1.新老都有子元素,采用双指针方式进行对比

                sameVnode 判断tag和key完全相同为同一节点,进行节点复用

                             头和头相等对比

                             尾和尾相等对比

                             头和尾相等对比

                             sameVnode 的时候传入两个新老子节点patch(oldChild,newChild)

                乱序情况 -- 上面的都不符合,先遍历旧子节点数组形成 key值映射的map对象。

                然后根据新子节点数组循环 按照key值和位置关系移动以及新增节点 最后删除多余的旧子节点 如果移动旧节点同样需要patch(oldChild,newChild)

                2.新的有子元素,老的没有子元素。-- 直接将子元素虚拟节点转化成真实节点插入即可。

                3.新的没有子元素,老的有子元素。 -- 直接清空 innerHtml

3、无 tag 标签 -- 文本节点直接比较内容是否一致

3、为什么要用key

在没有 key 的情况下,Vue 将使用一种最小化元素移动的算法,并尽可能地就地更新/复用相同类型的元素。如果传了 key,则将根据 key 的变化顺序来重新排列元素,并且将始终移除/销毁 key 已经不存在的元素。

同一个父元素下的子元素必须具有唯一的 key。重复的 key 将会导致渲染异常。 

例如:有元素 A B C D E ,当我想把元素变成 B C D E 时

没有key值时,key默认都是undefined,就会按照diff算法的就地复用来进行比较,它会把A更新成B,B更新成C,C更新成D,最后删除E

有唯一的key值时,B C D E全部复用,只删除A

明显可以看出,当没有key值时改变元素会产生许多DOM操作,而DOM操作是非常消耗性能的,尤其是当有多层嵌套时,消耗的性能可想而知。

4、什么情况下使用index作为key值会出问题

在我们实际使用中使用index作为key或者不写key值,看起来除了操作DOM更耗性能,好像没有出现什么问题。

当你的只是用来做数据展示的时候,确实是没有什么问题的,但当你的子元素包含输入文本时就会出现问题了。

DEMO

<template>
	<div>
		<div v-for="(item,index) in list" :key="index" style="background-color: palegoldenrod;margin: 10px;padding: 10px;">
			<div>{
   
   {item.name}}</div>
			<input type="input" placeholder="请输入"/>
			<span @click="handleAdd()" style="margin-right: 15px;">添加</span>
			<span @click="handleDelete(index)">删除</span>
		</div>
	</div>
</template>

<script>
	export default {
		data() {
			return {
				list: [],
			};
		},
		created() {
			this.handleAdd();
		},
		methods: {
			handleAdd() {
				let random = Math.random() * 1000;
				this.list.push({
					id: random,
					name: random
				})
			},
			handleDelete(i) {
				this.list.splice(i, 1)
			},
		}
	};
</script>

<style lang="scss" scoped>
</style>

此时我生成了三条数据

 当我点击删除第二条数据时,可以看到文本框的内容还是原本的第二条数据的内容

原因是虚拟DOM在比较元素的时候,因为DOM上的key等属性均未发生变化,所以其自身和内部的input均被复用了。

所以我们应该养成好习惯,不在实际开发过程中把index作为key值。

猜你喜欢

转载自blog.csdn.net/qq_39998026/article/details/126656058