如果你已经很熟悉Vue,一定也会很熟悉v-for指令。如果用过其他的前端框架的话,它们也都提供了在HTML模板中循环数据标记的方法。今天这篇文章让我们搞明白为什么key属性对于v-for这么重要。

我在刚开始写Vue时,也很少去写key 属性,因为它也不是必需的,不写也不会报错,也不会耽误数据渲染。但当项目中配了ESLint的规则,必需要写key时,我就统一的都把index索引作为key,然后接着开发,就像下面这样:

<h1 v-for="(item, index) in list" :key="index">{{ item.title }}</h1>

术语解读

那为什么我们需要在v-for循环中加key属性呢?

其实是因为Vue可以通过key这种方式来观察数组或者对象中的数据哪些正发生变化。v-for默认是通过index索引来追踪变化,所以上面我通过将index作为索引的做法是多余的,尽管它解决了ESLint的错误。通过追踪index索引,Vue观测列表中的项目的顺序变化,并在顺序发生变化时,就地修改每个项目。Vue文档中指出,“这个默认的模式是高效的”,但也指出并不适合所有的情况。

像Vue文档所说的那样,我们要尽可能使用数据的唯一ID来做每个元素的key,这样 Vue 可以更准确地追踪数组中项目的更改并更新组件状态,并且能够重用和重新排序现有组件,而不必重新渲染整个循环。

举例说明

上面说了那么多可能比较官方,还是不容易理解,下面我们通过一个例子来展示key属性的重要性。

假设我们有一个图书列表,然后有一个book-detail组件用来展示书的名称,和购买按钮,然后循环这个book-detail组件

book-detail组件代码如下:

<div class="book">
  <h1>{{ book.title }}</h1>
  <div v-if="isCheckedOut" class="label">立即购买</div>
</div>

组件展示这本书的书名,以及这本书是否能被购买。

组件的JavaScript部分:

export default {
  name: 'Book',
  props: ['book'],
  data() {
    return {
      isCheckedOut: null
    }
  },
  mounted() {
    this.checkBookStatus(this.book.id);
  },
  methods: {
    checkBookStatus() {
      axios.get(`/book-status/${this.book.id}`)
        .then(res => {
          this.isCheckedOut = res.data.isCheckedOut;
        })
    }
  }
}

我们通过props将书的信息传入,然后假设我们需要单独调用接口去查询这本书的状态,是否可以被购买。

然后循环出图书列表:

<book-detail v-for="(book, index) in books" :key="index" :book="book"></book-detail>

完整的在线例子地址:https://codepen.io/cmdfas-the-bashful/pen/yLoEmJq

这看起来并没有什么问题,在页面展示上也没有什么问题。到这里就结束了?并没有

假设用户可以向系统添加一本新书,并要求添加到列表顶部,而不是底部。如果添加到底部,不会有什么问题,因为index索引仍然是新的值,如果添加到列表顶部,那新加到顶部元素的索引将会之前第一本书的索引也就是0,依次类推,只有最后一本的索引为新索引。

能看出这里可能存在什么问题吗?如果Vue没有正确地将第一个索引追踪为新索引,那该组件永远不会重新挂载,所以checkBookStatus永远不会更新图书的状态,相反,isCheckedOut 的值将是前一本书的值。

那么要解决这个问题最简单的方式就是设置唯一的key值,我将book.id作为key值,Vue 将能够正确跟踪更改并将新组件添加到顶部并重新排序其余组件。

<book-detail v-for="(book, index) in books" :key="book.id" :book="book"></book-detail>

总结

明白了其中的原理,我们可以根据场景去选择使用v-for和key,如果只是将数据插入到数组末尾,或者只是简单的列表,那使用索引不会产生什么问题,因为没有排序,Vue可以正确的追踪到新的索引。这是一种很好的处理方式(也是 Vue 使用的默认方式)。

但是,当你带有props的组件中拥有组件自己的状态时,或者不只是在末尾添加内容的方式改变列表时,一定要使用唯一的id。

同样,有关 v-for 中使用的更多有用信息,建议查看 Vue 官方文档

Logo

前往低代码交流专区

更多推荐