为什么我的 Vue 组件需要 :key?

Why does my Vue component require :key?

我有一个小的 Vue.js 组件,它显示最喜欢的星形图标。单击图标 favorites/unfavorites 元素。到目前为止,我只实现了 UI 部分,如下所示:

<template>
  <div :key="favorite">
    <a v-on:click="toggleFavorite" style="cursor: pointer">
      <i v-show="favorite" class="text-warning fas fa-star"></i>
      <i v-show="!favorite" class="text-warning far fa-star"></i>
    </a>
  </div>
</template>

<script>
export default {
  data() {
    return {
      favorite: true,
    }
  },
  mounted() {
  },
  methods: {
    toggleFavorite() {
      this.favorite = !this.favorite
    }
  },
  props: ['team-id'],
}
</script>

<style scoped>
</style>

如您所见,逻辑非常简单。

这很好用,但让我困扰的一件事是,如果我从我的模板中删除 :key 属性,当我点击它时图标不会更新(即使我已检查基础 属性 是否 确实已正确更新)。添加 :key 使其工作,我想是因为它强制 Vue.js 在更新 favorite 时完全重新渲染组件。

为什么会这样?我对 JS 框架的世界相当陌生,所以请原谅我可能遗漏的任何明显的东西。我在网上做了一些研究,但找不到解释。我只是想确保我以正确的方式做事,而不仅仅是解决这里的问题。

好的,我认为这里的问题是您正在更改根数据对象。为了保持反应性,您不应在实例化 Vue 后更改根数据对象。

Here is your code 在一个简单的 Vue 中。我不需要 :key 来让它工作。我会保留 :key 用于内部循环。

标记

<div id="vueRoot">
  <a v-on:click="toggleFavorite" style="cursor: pointer">
    <i v-show="store.favorite" class="text-warning fas fa-star">Fav</i>
    <i v-show="!store.favorite" class="text-warning far fa-star">Not fav</i>
  </a>
</div>

代码

vm = new Vue({
  el : "#vueRoot",
  data() {
    return { store :{
      favorite: true
    }}
  },
  mounted() {
  },
  methods: {
    toggleFavorite() {
      this.store.favorite = !this.store.favorite
    }
  }
}
);

这是一个改动很小的工作示例。根据您向我们展示的内容,您应该只有 元素,然后使用动态 class 列表执行您想要的操作,例如...

<i :class="['text-warning','fa-star',store.favorite?'fas':'far']"></i>

您不需要 :key,它只在 v-for 循环中才需要。我建议您将其删除并将 v-show 替换为 v-if 和 v-else 指令。

  <i v-if="favorite" class="text-warning fas fa-star"></i>
  <i v-else class="text-warning far fa-star"></i>

v-if 删除该部分并将其添加到 DOM 而 v-show 只是隐藏它所以这样可以很好地解决您的问题

Vue 在必要时使用虚拟 DOM 修补程序。也就是说,每当 vue 检测到 DOM 上的更改时,它都会对其进行修补以提高性能。在 DOM 中修补不会更改图标或图像。您需要替换 DOM。

因此,vue 为我们提供了每当我们需要通过替换方法更改DOM 的方式时,我们可以使用:key 绑定。

因此,:key 绑定可用于强制替换 element/component 而不是重新使用它。

以下整个 html div 将在 favorite 数据发生变化时被替换,因为我们 :key 对其进行绑定:

<div :key="favorite">
    <a v-on:click="toggleFavorite" style="cursor: pointer">
      <i v-show="favorite" class="text-warning fas fa-star"></i>
      <i v-show="!favorite" class="text-warning far fa-star"></i>
    </a>
  </div>

这就是为什么 vue 强制允许我们在循环内使用 :key 绑定,因为当它检测到 data 中的变化时需要替换循环内的元素。这是从 2.2.0+ 强制执行的,ESLint 也实现了此功能,因此如果您在循环中错过 :key 绑定,那么当您使用支持 eslint 的编辑器时,您将在该行看到错误,以便您可以修复错误。

只是一个意见,应该从 vue 中删除 :key 绑定的严格要求,因为我们可能想要 predefined data 的循环并且不想更改 DOM 但我们仍然使用 v-for 循环来列出更大的数据。但这种情况可能很少见。


仔细阅读documentation for :key binding,你就会有一个想法。

:key 绑定在您需要时很有用:

Properly trigger lifecycle hooks of a component

Trigger transitions


  1. 使用:key绑定替换DOM。请记住它会降低性能,因为它会替换绑定到元素的整个 DOM。
  2. 当您不想替换 DOM 或时,请不要使用 :key 绑定 您认为不需要 data 更改检测。这会 让 vue 在没有 :key 绑定的情况下表现更好。

这似乎是 FontAwesome CSS 的一个普遍问题,与框架无关。 在 github 上有一个问题,这里有同样的问题与 react https://github.com/FortAwesome/Font-Awesome/issues/11967

为了证明这一点,这里是同一示例的简化版本,但使用了 bootstrap 个图标

new Vue({
  el: '#app',
  data() {
    return { 
      fav: true
    }
  }
});
<script
  src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"
></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">

<div id="app">
  <div>
    <a v-on:click="fav = !fav" style="cursor: pointer">
      <i v-show="fav" class="glyphicon glyphicon-star"></i>
      <i v-show="!fav" class="glyphicon glyphicon-star-empty"></i>
    </a>
  </div>
</div>