在 Vue 中,什么时候需要 :key 属性?为什么?GenAI LIVE!| 2025 年 6 月 4 日

2025-06-09

在 Vue 中,我什么时候真正需要 :key 属性以及为什么?

GenAI LIVE! | 2025年6月4日

本文最初发表于https://www.telerik.com/blogs/in-vue-when-do-i-actually-need-the-key-attribute-and-why


问题

你可能正在编写一个 Vue 应用。也许你正在使用强大的Vue CLI 3,并且已经设置好了相应的设置,可以显示一些 eslint 错误和提示。

突然间,你正专心致志地吃着牛油果吐司和早晨的拿铁,那些弯曲的线条吸引了你的注意力。最后一个v-for循环好像是错的

也许你决定忽略它,继续享受牛油果带来的极乐世界,但它又一次袭来。控制台错误🚧。你有点慌了,Vue 正在要求它。:key尚未设置。

你屈服于直觉,添加了一个:key基于数组循环的代码,你知道它必须是唯一的。甜蜜的解脱,错误消失了,你又可以继续通过 JavaScript 探索人类的进步了。

可是,这一切究竟意味着什么?你又何必在意呢?

:key了解🔑的基础知识

正如官方文档所建议的,keyVue 使用特殊属性作为提示以了解您到底想要完成什么。

但是,这只是一个提示,究竟是什么意思呢?Vue 很智能。如果你没有:keyv-for循环中添加这个属性,应用程序也不会崩溃。实际上,你甚至不需要添加它。

:key缺失时,Vue 将使用内部函数或算法来尝试找出避免移动 DOM 元素的最佳方法。更少的移动意味着更少的重新渲染和更好的性能。

然而,这个过程存在通用性和自动化的缺陷,尽管它本身就很出色,但你,作为程序员💻,可能更了解性能和 DOM 操作应该如何进行。这意味着你必须理解属性才能真正获得所需的结果。

那么为什么我们会收到eslint警告和控制台警告呢?⚠️

在某些特殊情况下,使用key至关重要,例如为了确保数据一致性且不丢失值(例如在表单中),或者为了在动画中保持对象恒常性。稍后将详细介绍这些内容。

在这种情况下,我个人的建议是,您在每种情况下都继续使用它,但要更好地理解它将完成什么以及为什么需要添加它。

我们来谈谈具体情况。

保存状态

当处理循环中具有状态v-for的 HTML 元素时,我们必须小心,以免在重新渲染 DOM 时破坏该状态。

<input><select>和这样的元素<textarea>都持有一个内部状态,用于捕获value该元素的 。当 Vue 的虚拟 DOM 因为我们的响应式数据发生变化而被修改时,如果 未正确设置 ,则可能会出现保存循环元素的 DOM 被完全或部分破坏的情况key

<!-- Wrong -->
<input v-for="input in myForm" />

<!-- Right -->
<input v-for="input in myForm" :key="unique-condition" />

如果您不清楚自己在寻找什么,这个问题将导致非常难以调试的情况,因为它可能只是“看起来”像是您从表单收集的数据被神奇地删除了。

同样的情况也适用于循环遍历使用该v-html指令的元素。该key属性将帮助 Vue 更好地识别列表中的每个元素,并且不会破坏可能包含状态元素的元素。

<!-- Wrong -->
<span v-html="<input />" v-for="item in items" />

<!-- Right -->
<span v-html="<input />" v-for="item in items" :key="unique-condition" />

当然,这也适用于循环自定义的、保存状态的组件,同样的经验法则也适用。如果key没有定义,则数据和状态可能会因 DOM 重新渲染而被破坏。

最后,留意在v-for包含状态元素的元素上循环的循环。显然,同样的问题也可能发生。

<!-- Wrong -->
<div v-for="item in items">
    <input>
</div>

<!-- Right -->
<div v-for="item in items" :key="unique-condition">
    <input>
</div>

客体恒常性

动画不仅仅是一种美观的数据移动方式🎬,它还能向用户传达重要信息,告诉他们正在查看的信息发生了什么变化。当一个对象在屏幕上移动、滑动或淡入淡出时,我们希望该对象保持一致且易于跟踪,因为它能够传达它试图向我们展示的信息。

等等,什么?

想象一下,当您触摸汉堡包🍔图标后,移动菜单从左侧📲滑入(🤔我们有汉堡包和烤肉串菜单,让我们一起制作🥑菜单吧!)。

它流畅地过渡到屏幕中间,清晰地显示了用户浏览网页的选项。然而,当你点击其中一个菜单项时,菜单却神奇地弹到了屏幕右侧,消失在手机的右侧。

你感到困惑,点击了汉堡图标,菜单再次从屏幕左侧出现。🤷‍

这是缺乏对象恒常性的一个很好的例子。我们期望菜单的虚拟对象在手机的同一侧“隐藏”,并在我们按下按钮时“滑入”视口。如果这种动画不一致或不清晰,就会造成糟糕的用户体验,也会导致信息追踪问题。

这是一个非常简单的例子,但如果我们更进一步,拥有一个试图传达诸如图表数据或待办事项列表等信息的列表,会发生什么情况呢?当其中一个项目向左滑动或淡出时,我们预期该项目会消失。如果由于某种用户未知的原因,该对象神奇地消失了,然后另一个对象向左滑动,这会造成混乱,而动画效果——它不但不能提供强烈的视觉提示,反而会造成不适和困惑。

一个真实的例子

空谈无益,代码见仁见智。—— Linus Torvalds

我创建了一个我描述的最后一个用户交互的简化示例,以便您可以看到它的实际运行。

https://codesandbox.io/s/jjlwv87w1v

打开沙盒并查看App.vue文件。

我们有两个由同一个数据池提供的数据项列表,该属性称为list

在顶部列表中,我们创建一个v-for循环,使用id每个项目的唯一属性来跟踪每个列表项的唯一性 - 通常由编译器建议,并提高 DOM 性能。

在底部列表中,我们使用一种常见的“技巧”,使用数组index作为循环项目并满足警告的方式:key

我不会深入探讨使用index作为键对 DOM 的影响,因为如果你确切地知道如何管理索引,有时它可能是正确的答案。相反,让我们专注于它对用户体验的影响。

这两个列表都封装在一个<group-transition>组件中,这样我们就能直观地识别正在发生的事情。继续操作顶部列表,点击几个对象,然后点击reset按钮。很流畅,对吧?你点击的对象就是正在滑走的对象。任务完成。

现在继续点击第二个列表。我不知道你的情况,但对我来说,这似乎有问题。

Chris Fritz 有一个精彩的例子,展示了流畅的动画如何带来直观的用户体验。请务必在这个 fiddle 中查看。另外,尝试一下:key,如果你破坏了系统,数字动画就会停止。

请记住,对于最后一个例子,<group-transition>如果删除,它实际上会引发警告key,并且渲染也会完全中断。

尝试向循环添加一个索引v-for并将其设置为的值:key,就像某些人所做的那样,以“满足”条件并消除警告。

打破现状

第二个例子中,究竟发生了什么,导致对象恒常性被破坏?🔎

当我们点击其中一个项目来触发该removeFromList方法时,Vue 会在后台执行几件事。首先,该方法通过调用 的索引来更新array保存我们的listspliceitem

然而,一旦list📝更新,Vue 必须重新渲染 DOM 才能对状态的变化做出反应,这是 Vue 反应性的核心。

通常,Vue 会知道,对于一个v-for循环,它需要通过 来确定需要更新哪个元素key,这是您已经知道的。但是,由于 ,<transition-group>Vue 保留了部分状态副本以在元素从屏幕中移除时执行动画,即使该元素在实际组件的 上不再存在state
当我们:key在第一个示例中使用对象的 id 时,Vue对我们要完成的任务有一个精确的item引用,因为这个特定的对象有一种唯一的方式来标识自己。因此,当 Vue 需要从状态和动画中移除它时,它可以准确地知道需要处理哪一个。

然而,当我们使用:key索引时,我们遇到了一个问题。还记得我们刚刚一步步讲解的吗?让我们再试一次,但先仔细看看索引到底做了什么。

  1. 我们点击一​​个项目 - 让我们以id2 为例。
  2. removeFromList方法发现此项的索引实际上是1,并立即从数组中删除此项。
  3. Vue 知道由于更新了 DOM,它需要重新渲染list,并尽力找出需要在屏幕上重绘的项目。因此,它从索引开始1(循环数组),看起来似乎没有变化。它继续遍历索引1,发现内容发生了变化(索引 2 中的内容现在在索引 1 中,因为 splice 操作将它们全部向下移动了一位)。然后继续遍历索引2,同样的问题再次出现,依此类推。Vue 有效地重新渲染了整个列表。
  4. 另一方面,<transition-group>它正在尽力跟上 DOM 的变化和state修改,并尽力将已删除的项目“复制”到列表末尾,并以动画方式使其离开屏幕。它无法知道如何重新排序其内部状态以适应状态中的索引变化。

总结

这个key属性的内涵远比表面看起来的要丰富得多。既然您已经确切了解了它想要实现的目标,以及其背后的“魔力”,那么在开发循环时,您就可以做出更明智的调用,并对应用程序及其性能进行更精细的控制。💪

与往常一样,感谢您的阅读,请在 Twitter 上通过@marinamosti与我分享您的想法。

附言:向神奇的牛油果致敬🥑

附言:❤️🔥🐶☠️

链接链接 https://dev.to/marinamosti/in-vue-when-do-i-actually-need-the-key-attribute-and-why-ga5
PREV
后端代码审查清单结论
NEXT
Vue.js 初学者实践(第六部分)当简单的 prop 不够用时实际的计算属性方法 vs 计算属性