Vue中使用v-for时key值绑定删除元素总是最后一个

在vue中使用v-for的时候需要给每个item绑定一个唯一的值,这个值可以是number或者string类型的值。key的作用主要是用在Vue的虚拟dom算法,在新旧nodes元素对比时辨识。Vue会基于key的变化重新排列元素顺序,并且会移除key不存在的元素。重复的key会造成渲染错误。这边演示一个删除一个元素后渲染出错的情况。

看下面一段代码:

<!-- 父组件 -->
<template>
  <div id="app">
    <children v-for="(item,index) in list" :item='item' :index='index' :key='index' @removeItem='removeItem' />
    <el-button @click="addItem">添加</el-button>
  </div>
</template>

 <!-- 子组件 -->
<template>
    <el-form ref="form" :model="form" label-width="80px">
        <el-form-item label="姓名">
            <el-input v-model="form.name"></el-input>
            <el-button class="button" @click="remove">删除</el-button>
        </el-form-item>
    </el-form>
</template>
  // 父组件data
  data(){
  	return{
  		list:[
  			{
  			 	name:'乔治1',
  			},
  			{
  			 	name:'乔治2',
  			},
  			{
  			 	name:'乔治3',
  			},
  		]
  	}
  },
  // 父组件methods
  methods:{
  	// 删除元素
    removeItem(i){
      this.list.splice(i,1)
    },
    // 添加元素
    addItem(){
      var num = this.list.length + 1
      this.list.push({name:'乔治' + num})
    },
  },

  //下面是子组件
  		props:{
            item:{
                type:Object
            },
            index:{
                type:Number
            }
        },
        data(){
            return{
                form:{}
            }
        },
        created() {
            this.form = this.item // 注意这个赋值
        },
        methods: {
            remove(){
                this.$emit('removeItem',this.index)
            }
        },

页面效果如下:
在这里插入图片描述
点击第一个元素的删除按钮查看效果如下:
在这里插入图片描述
这边看到的是第三个元素被删除了,我们打印一下数组list,看看数组是否正确,可以看到,数组是正确的,但是展示的还是错的。

在这里插入图片描述
这边对代码进行改动,因为删除第一个元素的时候,第一个元素绑定的key值是下标0,删除第一个后,第二个的下标就会变成0,依次类推,最终缺少的下标是最后一个元素的下标。所以就把最后一个元素删除了(展示效果)。现在我们把key值绑定换一种方式。
代码如下:

<template>
  <div id="app">
    <!-- 父组件 -->
    <children v-for="(item,index) in list" :item='item' :index='index' :key='symbol(index)' @removeItem='removeItem' />
    <el-button class="add-button" @click="addItem">添加</el-button>
  </div>
</template>
<!-- 把key值绑定成es6的symbol唯一值类型,把下标传进去转换为唯一值绑定成key -->

symbol(i){
   return Symbol(i)
},

这边再次测试,再次删除第一个元素,查看效果,效果图如下:
在这里插入图片描述
可以看到删除已经成功了。
但是如果这样绑定key值,表面上是看不出什么问题的,现在我在子组件的生命周期里放一层打印,就可以看出效果

created() {
    this.form = this.item
    console.log('组件创建!')
},

查看效果:
在这里插入图片描述
可以看到,删除成功后走了剩下两个子组件的生命周期,证明剩下的组件全都重新创建了一遍,因为key值变化的原因导致,因为symbol数据类型每次都不一样,每次数组长度发生变化都会重新绑定key值,导致每次数组长度发生改变,触发v-for比较key值的操作,但是key值不一样,会重新创建组件,现在还只是一个小表单,实际开发中可能小的组件中有需要发送请求的操作,或者小组件数量较多,这种重渲染会产生性能问题,就违背了绑定key值的初衷,所以这样绑定还是不够好。如果是数组长度不变的遍历是可以使用symbol绑定key值的。现在我们把key值绑定再次换回index绑定,但是子组件需要做一些改动
代码如下:

<!-- 父组件 -->
<template>
  <div id="app">
    <children v-for="(item,index) in list" :item='item' :index='index' :key='index' @removeItem='removeItem' />
    <el-button class="add-button" @click="addItem">添加</el-button>
  </div>
</template>

<!-- 子组件 -->
<template>
<!-- 子组件表单绑定的数据依赖传递过来的item -->
    <el-form ref="form" :model="item" label-width="80px">
        <el-form-item label="姓名">
            <el-input v-model="item.name"></el-input>
            <el-button class="button" @click="remove">删除</el-button>
        </el-form-item>
    </el-form>
</template>
created() {
   // this.form = this.item 这一行代码注释掉,数据直接依赖父组件传递的数据
   console.log('组件创建!');
},

测试效果如下:
在这里插入图片描述
可以看到组件没有再次创建,页面渲染也没有出错。不过实际项目中可能会有不同。
在我们正常的业务场景中,这样的表单数据,或者数据都会有id属性,每一条数据都有自己的id,可以直接把key值绑定为id,如果是多表单提交的情况下,需要把表单抽成组件使用,这个时候新建表单是没有id的,我这边需求是新建表单的id传递给后端一个为0的id,这样新建的表单就会出现id相同的情况,可以使用负值的id进行绑定,提交的时候判断id是否大于0,如果小于零就把id换成0提交,不小于则直接提交,就可以直接把key绑定为id了,然后在子组件的生命周期里赋值也可以正常渲染。这个官方不建议使用index绑定key值,上面绑定却正常有效,如果有比较了解原理的大佬还望评论区指点,不胜感激!

Logo

前往低代码交流专区

更多推荐