在Vue.js 3中使用钥匙与v-for的提示和问题

48 阅读4分钟

在Vue中使用v-for时,通常建议提供一个特殊的key 属性。类似这样的东西。

<div v-for="item in items" :key="item.id">

这个关键属性的目的是为 "Vue的虚拟DOM算法提供提示,以便在将新的节点列表与旧的列表进行比较时识别VNodes"(来自Vue.js Docs)。

从本质上讲,它帮助Vue识别哪些是变化的,哪些是没有变化的。因此,如果没有必要,它不必创建任何新的DOM元素或移动任何DOM元素。

在我使用Vue的整个过程中,我看到了一些围绕key属性的误解(以及我自己对它的大量误解),所以我想提供一些关于如何和如何不使用它的提示。

  • 请注意,除非另有说明,下面的所有例子都假设数组中的每一项都是一个对象。

就这样做

首先,我最好的建议是:尽可能多地提供它。这是Vue的官方ES Lint插件所鼓励的,其中包括一个vue/required-v-for-key 规则,从长远来看可能会给你带来一些麻烦。

:键应该是唯一的(通常是一个id)

好的,所以我应该使用它,但如何使用呢?首先,key属性对于被迭代的数组中的每个项目来说都应该是一个唯一的值。如果你的数据来自数据库,这通常是一个简单的决定,只需使用iduid 或任何它在你的特定资源上的调用。

<div v-for="user in users" :key="user.id">

:键应该是唯一的(没有id)

但是,如果你的数组中的项目不包括一个id呢?那么键应该是什么?好吧,如果有另一个保证是唯一的值,你可以使用它。

<div v-for="user in users" :key="user.username">

或者,如果每个项目的单一属性都不能保证是唯一的,但几个不同的属性的混合物是唯一的,那么你可以对它进行JSON编码。

<div v-for="user in users" :key="JSON.stringify(user)">

但是,如果什么都不能保证,那怎么办呢?我可以使用索引吗?

不能用索引作为键

你不应该使用数组的索引作为键,因为索引只表示一个项目在数组中的位置,而不是项目本身的标识符。

❌ <div v-for="(user, index) in users" :key="index">

为什么这很重要?因为如果一个新的项目被插入到数组中的任何位置,而不是末端,当Vue用新的项目数据修补DOM时,那么迭代组件中的任何本地数据将不会随之更新。

如果没有实际看到,这很难理解。在下面的CodeSandbox中,有两个相同的列表,除了第一个使用index 作为键,而下一个使用user.name 。 "Add New After Robin "按钮使用splice将猫女插入到列表的中间。继续按下去,并比较2个列表。

注意到本地数据现在在第一个列表中是如何完全偏离的。这就是使用:key="index" 的问题。

那么,如果不使用钥匙呢?

让我告诉你一个秘密,把它关掉和使用:key="index" 是完全一样的。因此,不使用它和使用:key="index" 一样糟糕,只是你不会有错误的印象,以为你提供了钥匙,你就受到了保护。

因此,我们又回到了ESLint规则的字面上,要求我们的v-for 使用key

然而,我们仍然没有解决当我们的项目确实没有什么独特之处时的问题。

当没有什么是真正独特的时候

我想这是大多数人真正被卡住的地方。因此,让我们从另一个角度来看看这个问题。什么时候可以不提供钥匙。有几种情况下不提供钥匙是 "技术上 "可以接受的。

  • 被迭代的项目不产生需要本地状态的组件或DOM(即组件中的数据或DOM元素中的输入值)。
  • 或者如果这些项目永远不会被重新排序(作为一个整体或者通过在列表末尾以外的任何地方插入一个新项目)。

虽然这些场景确实存在,但很多时候,随着功能的变化和发展,它们会演变成不符合上述要求的场景。因此,不使用钥匙仍然可能有潜在的危险。

结论

总之,根据我们现在知道的所有情况,我的最终建议是。

在以下情况下不要使用钥匙:

  • 你没有独特的东西,而且
    • 你正在快速测试一些东西
    • 这是一个简单的v-for演示
    • 或者你正在迭代一个简单的硬编码数组(不是来自数据库的动态数据,等等)。

包括键:

  • 每当你有一些独特的东西时
  • 你所迭代的不仅仅是一个简单的硬编码的数组
  • 甚至当没有独特的东西,但它是动态数据时(在这种情况下,你需要生成随机的唯一ID)