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

Wrong item deleted in v-for component [choosing the right :key in v-for]

我有如下两个组件:

     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(`${index}th element deleted! `);
               
             }
         }
     });
     let app = new Vue({
         el: '#app'
     });
<script src="https://unpkg.com/vue"></script>

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

在这个演示中,无论你点击哪个项目,最后一个项目总是被删除。

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

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'
                 }]
             };
         },

检查此 fiddle:demo

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

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

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

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

你的问题因一些你在自然界中不常见的小事而变得更糟,你应该避免...在创建的文件中将 parentData.name 分配给 childData.name钩子:因为你的 children 被重用,而不是重新创建,所以 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(`${index}th element deleted! `);
               
             }
         }
     });
     let app = new Vue({
         el: '#app'
     });
<script src="https://unpkg.com/vue"></script>

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