【key值】为什么不能相同?
这篇文章讨论了React中key值的重要性和使用中的注意事项。首先解释了key值的定义和作用,强调其在虚拟DOM和diff算法中的关键作用。接着通过示例展示了不使用key值可能导致的性能问题和数据冗余,特别是当key值重复时,会导致组件复用失败和数据混乱。文章最后总结了key值的重要性,并提醒开发者在使用key值时需谨慎,避免重复值带来的问题。
今天是个讨论帖,我想跟大家谈论下key值的真正意义
首先给还不了解key的同学简单说一下什么是key,又是干什么用的?
许多开发者,包括新手,对于key值的不同看法,我们都知道,关于key值的讨论从未间断过。举个例子来说,有的开发者认为key值应该唯一,有的则认为可以重复使用。
1,渲染列表是一定要添加唯一key。
2,如果不涉及对列表的增删问题,也可以用index作为key。
等等,就不一一赘述了。
1,不写key值,会出现什么问题?
在开发React项目时,若在map组件中渲染列表却未为每个元素指定唯一的键值,建议您检查控制台以获取相关提示信息,例如。
const [list,setList]=useState([1,2,3,3])
return(
{
list .map((item)=>{
return(
<li>{item}</li>
)
})
}
)

(这句话的意思是每个列表元素都应该有一个key值)
那么问题来了,什么是key值?又是用来干什么的呢?
想要了解key值,那得先了解一下react的一些基础知识
虚拟DOM:让开发者专注于业务逻辑的实现,通过数据驱动页面呈现,而非频繁操作DOM。
Diff算法 :虚拟dom=>真实dom
该算法是通过将key值作为唯一的标识符,比较组件的value来判断是否需要更新的。因此,如果没有key,该算法将无法确定如何更新组件。例如,在下面的示例中
eg.
<li>1</li> <li>11</li>
<li>2</li> => <li>2</li>
<li>3</li> <li>3</li>
我们需要将左边的列表更新为右边的,但如果没有使用key值作为标识符,React会将旧的节点全部销毁,并生成新的节点。然而,这与我们预期的效果相比相差甚远,因为我们只更改了一个li节点。
但是当存在key值时,我们可以在diff操作中同时对比key和当前节点的type,如果两者完全一致,则该节点具有复用性,仅需对属性值进行更新即可,而不必进行节点的销毁和重建。通过加入key值,我们能够更直观地发现左右两边key和元素类型的一致性,即列表1的value发生了变化,而列表2、3的元素类型保持不变,因此我们只需更新列表1的value部分,而列表2、3的复用性得以保留,无需进行销毁和重建操作。
让我们总结一下,React根据key值来决定组件的销毁、重建还是更新的原则:组件的销毁、重建或更新,具体取决于key值的大小关系:如果key值较大,则优先进行销毁操作;如果key值较小,则优先进行重建操作;如果key值相等,则优先进行更新操作。
1,key相同,组件无变化,直接复用。
2,key相同,组件有所变化,智慧更新组件对应变化的属性。
3,key不同,组件会销毁之前的组件,将整个组件重新渲染
ok,上面讲了key的重要性,那我们来看一看key值重复的危害。
key值重复的危害
eg1.
const [list,setList]=useState([1,2,3,3])
return(
{
list .map((item)=>{
return(
<li key={item}>{item}</li>
)
})
}
)
在本实现中,我们采用数组值作为键,系统会检测到3的重复性,这将导致控制台首先输出警告信息。

不过页面渲染好像没什么问题,好,那我们接着往下看
2,这次我们来模拟一个分页器
const [list,setList]=useState([1,2,3,3,4])
return(
<div>
{
list .map((item)=>{
return(
<li key={item}>{item}</li>
)
})
}
{/* 点击下一页,更改list的内容,模拟翻页 */}
<button onClick={()=>setList([2,3,3,4,5])}>下一页</button>
</div>
)
翻页前

翻页后

我们可以看到,拥有重复key值的3,在翻页后多了一个。
在实际开发中,功能优势也会在页面刷新后列表数据出现混乱的问题,主要由于key值重复所导致。
那为什么3会多一个呢?
先来说下我的推测:在diff执行过程中,右边的前两个3的key和value与左侧第一个完全相同,因此可以将其直接复用。至于左侧的第二个3,虽然没有被复用,但也不会被销毁,因为销毁的条件是新旧元素在key和value上存在差异。显然,第二个3的key和value与前两个3完全相同,因此不会被销毁,从而得以保留。为了更好地理解这一过程,我绘制了相应的流程图。

ok,那我换一批数看看,接着往下看这个例子
const [list,setList]=useState([1,2,3,3,4])
return(
<div>
{
list .map((item)=>{
return(
<li key={item}>{item}</li>
)
})
}
{/* 点击下一页,更改list的内容,模拟翻页 */}
<button onClick={()=>setList([5,6,7,8,9])}>下一页</button>
</div>
)
翻页前

翻页后

这次前后两次数据完全不同,为什么3也被保留了下来呢?
我的最初猜测并不成立,然而,我对此仍然感到困惑。于是,我查阅了一些资料,了解到另一种观点:新旧元素的键和值均不一致,因此,需要逐一删除旧元素。然而,由于后面存在两个相同的键,在执行删除逻辑时,提前终止了,最终仅保留了一个元素。
听起来有点困惑的是,这种说法并没有解释为什么key值相同导致无法删除。他的意思是,因为key值相同,所以第二个无法被删除。听起来有点生硬的是,他的解释方式让人觉得不够自然,不过我也不太在意,毕竟1+1为什么等于2的道理大家都懂。
希望路过的大佬们可以留下您宝贵的见解
