在 v-for 组件中删除了错误的项目 [在 v-for 中选择正确的 :key]

Posted

技术标签:

【中文标题】在 v-for 组件中删除了错误的项目 [在 v-for 中选择正确的 :key]【英文标题】:Wrong item deleted in v-for component [choosing the right :key in v-for] 【发布时间】:2019-01-16 12:38:00 【问题描述】:

我有两个组件,如下所示:

     Vue.component('comp-child', 
         template: `<div>childData.name<slot></slot>randomNum</div>`,
         props: 
             parentData: 
             
         ,
         data() 
             return 
                 childData: ,
                 randomNum: Math.round(Math.random() * 100)
             ;
         ,
         created() 
             this.childData.name = this.parentData.name;
         
     );
     Vue.component('comp-parent', 
         template: `<div><component v-for="(item, index) in arr" is="comp-child" :key="index" :parent-data="item">
             <button @click="deleteItem(index)">delete</button>
         </component>
         </div>`,
         data()
             return 
                 arr: [
                     name:1
                 , 
                     name:2
                 , 
                     name:3
                 , 
                     name:4
                 , 
                     name:5
                 ]
             ;
         ,
         methods: 
             deleteItem(index) 
                 this.arr.splice(index, 1);
                 console.log(`$indexth element deleted! `);
               
             
         
     );
     let app = new Vue(
         el: '#app'
     );
<script src="https://unpkg.com/vue"></script>

<div id="app">
    <comp-parent></comp-parent>
</div>

在这个demo中,无论你点击哪个item,最后一个item都会被删除。

我定位到这个问题是v-forkey引起的,如果使用1, 2, 3, 4,..作为key,会出现这个问题,但是使用其他值作为key,比如string,它就可以正常工作;

template: `<div><component v-for="(item, index) in arr" is="comp-child" :key="item.key" :parent-data="item">
             <button @click="deleteItem(index)">delete</button>
         </component>
         </div>`,
         data()
             return 
                 arr: [
                     name:1,
                     key: 'key1'
                 , 
                     name:2,
                     key: 'key2'
                 , 
                     name:3,
                     key: 'key3'
                 , 
                     name:4,
                     key: 'key4'
                 , 
                     name:5,
                     key: 'key5'
                 ]
             ;
         ,

检查这个小提琴:demo

是不是虚拟DOM造成的?似乎 VUE 将 key 和子组件绑定为缓存,当 arr 更改时,它只是按照 index(1,2,3,..) 的顺序重新渲染组件,如果 arr 中的某些项目被删除,则arr 的长度减少导致最后一个无法渲染。

请有人给我解释一下,谢谢!

【问题讨论】:

您应该传递密钥以从数组中删除一个项目。因为 Vue 文档建议不要使用索引作为键 @latovic,如果密钥是 1,2,3……,通过密钥删除项目仍然会导致这个问题,不是吗?所以我想知道为什么使用索引删除不起作用 【参考方案1】:

当您使用 v-for 时,您的密钥必须是相关项目独有的一些数据。索引不好,因为它不能识别项目。只需将 key 更改为 item.name 即可让您的示例完美运行。

在您的示例中,您的源数组已被正确修改,但 vue 正在重用先前生成的组件实例来显示修改后的数组,并且这些实例具有剩余状态。 Vue 出于性能原因这样做,这通常不是问题,但它确实突出了选择正确键的重要性。

由于您在自然界中不经常看到的一些小事情,您的问题变得更糟,您应该避免......在 created 钩子中将 parentData.name 分配给 childData.name:因为您的孩子是被重用,而不是重新创建,childData.name 变得陈旧。随机数也是如此。

Vue.component('comp-child', 
         template: `<div>childData.name<slot></slot>randomNum</div>`,
         props: 
             parentData: 
             
         ,
         data() 
             return 
                 childData: ,
                 randomNum: Math.round(Math.random() * 100)
             ;
         ,
         created() 
             this.childData.name = this.parentData.name;
         
     );
     Vue.component('comp-parent', 
         template: `<div><component v-for="(item, index) in arr" is="comp-child" :key="item.name" :parent-data="item">
             <button @click="deleteItem(index)">delete</button>
         </component>
         </div>`,
         data()
             return 
                 arr: [
                     name:1
                 , 
                     name:2
                 , 
                     name:3
                 , 
                     name:4
                 , 
                     name:5
                 ]
             ;
         ,
         methods: 
             deleteItem(index) 
                 this.arr.splice(index, 1);
                 console.log(`$indexth element deleted! `);
               
             
         
     );
     let app = new Vue(
         el: '#app'
     );
<script src="https://unpkg.com/vue"></script>

<div id="app">
    <comp-parent></comp-parent>
</div>

【讨论】:

如果没有唯一字段,有什么建议可以用作键吗? 如果数组中没有任何内容,您始终可以为数组分配唯一键。键是保持 DOM 与模型中的数组同步的基本要求。

以上是关于在 v-for 组件中删除了错误的项目 [在 v-for 中选择正确的 :key]的主要内容,如果未能解决你的问题,请参考以下文章

删除当前元素后 v-for 列表更新不正确

vue组件5 组件和v-for指令

在 v-for 循环中使用 vue 组件

vue使用v-for渲染完组件,删除数据时总是删除最后一个解决方案

v-for

带有工厂功能的Vue组件v-for