阅读 459

Vue v-for的key值如何设置?

Vuev-for的key值到怎么设置?

在Vue项目中,v-for所在的DOM上,如果不设置key值,编辑器会警告;如果开启了eslint,eslint检查会提示需要给v-for设置key值 。我在做代码CR时,发现很多同学喜欢设置key值为唯一标识,像以下代码第二行设置为item.id。可能你也试过设置:key="index"也没有问题。那么Vue的v-for key值到底怎么设置?

<div v-for="item in list"></div>
<div v-for="item in list" :key="item.id"></div>
<div v-for="(item,index) in list" :key="index"></div>
复制代码

当我不设置key时

上代码!

<div id="app">
  <ul>
    <li v-for="item in list">
      <span>{{item.name}}</span>
      <input type="text" />
    </li>
  </ul>
  <button @click="exchange">交换</button>
</div>
<script>
  const app = new Vue({
    el: "#app",
    data: {
      list: [
        {
          id: 111,
          name: "杨倩",
        },
        {
          id: 222,
          name: "杨皓然",
        },
      ],
    },
    methods: {
      exchange() {
        this.list.reverse();
      },
    },
  });
</script>
复制代码

list数组有两对象,渲染成两行,每一行包括name和一个输入框;另外有一个“交换”按钮,点击会交换数组两个对象。

以下图片分别为“交换”前后。可以看到当数组元素交换时,界面元素也交换了。

image.png

image.png

有没有DOM交换

我们使用Vue尽量不要操作DOM,因为Vue代替我们操作DOM。那么当数组元素交换时,Vue是如何操作DOM,进而交换界面元素呢?两种方式:

  1. DOM交换
  2. DOM不交换,更新DOM的值

image.png

所以究竟有没有DOM交换? 如果DOM不交换,更新DOM的值的话,只需要更新两个span标签的值,而input是无须交换的。这样我们在输入框中分别输入1,2,再点击“交换”按钮,观察到:

image.png

image.png

input标签的值并没有变,也就是并没有交换liDOM !

想进一步证明,你也可以用js获取li所在的dom元素,交换后,再重新获取,用===对比。测试结果一样,并没有交换DOM

v-for key设置为index

我们再修改代码,把v-for key值设置为index,观察下是否会交换DOM

<li v-for="(item,index) in list" :key="index">
  <span>{{item.name}}</span>
  <input type="text" />
</li>
复制代码

观察到交换前后和上面情况一致,input输入框的值也没有交换,也就是说并没有交换DOM

v-for key设置为唯一标识

我们再修改代码,把v-for key值设置为item.id,观察下是否会交换DOM

<li v-for="item in list" :key="item.id">
  <span>{{item.name}}</span>
  <input type="text" />
</li>
复制代码

image.png

交换前后input输入框的值也交换了,也就是说数组元素交换,对应渲染的DOM也交换了!

分析原因

v-for的key值三种设置方式,只有设置为唯一标识时,DOM才交换,为什么呢?很简单,因为DOM交换耗性能,而Vue懒! 如果Demo没有input框,交换数组元素时,需要交换name,那么交换DOM和不交换DOM方式都是正确的。 当 Vue 正在更新使用 v-for 渲染的元素列表时,它默认使用“就地更新”的策略,Vue官方文档链接

解释以上三种key值设置

  1. 不设置key值。默认使用“就地更新”,数组元素更新,更新对应的DOM的值。

2.设置key值为index。设置key值,Vue能区分已渲染的DOM,但是数组交换后改变了两个元素各自的下标。所以原下标为1的元素,变成下标为0的元素,渲染原下标为0的DOM,无须交换DOM。 3.设置Key为item.id。item.id是唯一标识,Vue能区分已经渲染的DOM,元素交换后,DOM也会交换。

vfor的key设置为唯一标识效率高?!!

如果是的话,vfor内部实现时,怎么不直接设置key=Date.now(),即默认唯一标识。如果Vue源码没有实现,我们在写一个指令替换vfor即可。所以之所以Vue源码默认不设置vfor的key值为唯一标识,就是因为大多数情况下,就地更新效率更高!!! 我们使用v-for的大多数场景是这样: 声明data属性为空数组,请求赋值,在结构中使用vfor渲染;再多一点逻辑就是更新数组元素的某一项和在给数组末尾新增一项。 想象一下以上场景中,Vue是如何进行DOM操作的。赋值就新增DOM元素,数组增加元素也是新增DOM元素,如果某元素更新呢?数组元素下标和已经渲染的DOM下标一致,找到然后更新值即可!

<div id="app">
  <!-- 在结构中使用vfor渲染list -->
  <div v-for="item in items" :key="item.id">
    {{ item }}
  </div>
</div>
<script>
  const app = new Vue({
    el: "#app",
    data: {
      list: [],
    },
    created(){
      // 请求获取数据,赋值给list
      this.list = this.$request(...)
    }
  });
</script>
复制代码

当然如果是给数组中间插入一个元素,key值为唯一标识的确效率高些。这个和虚拟DOM有关,中文技术的世界这块资料已经泛滥泛滥,大家自行查找。这可能也是常见代码里面key值设置为唯一标识的原因。

结论

关于v-for的key值设置

  1. 如果已渲染的列表元素下标不会有变化,key值设置为index即可(防止eslint警告)
  2. 如果已渲染的列表元素下标会有变化,key值请设置为唯一标识
文章分类
前端
文章标签