Неправильный элемент удален в компоненте v-for [правильный выбор: введите 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>

В этой демонстрации, независимо от того, какой элемент вы щелкнете, всегда удаляется последний элемент.

Я обнаружил эту проблему, вызванную key из v-for, если использовать 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'
                 }]
             };
         },

проверьте эту скрипку: демонстрация

Это вызвано виртуальным DOM? Кажется, что VUE связывает ключ и дочерние компоненты как кеш, когда arr изменяется, он просто повторно отображает компоненты в порядке index (1,2,3, ..), если какой-то элемент в arr удален, длина arr уменьшается потому что последний не может быть отрисован.

Пожалуйста, объясните мне это, спасибо!


person Ian Zhong    schedule 09.08.2018    source источник
comment
Вы должны передать ключ, чтобы удалить элемент из массива. Поскольку документация Vue рекомендует не использовать индекс в качестве ключа   -  person latovic    schedule 09.08.2018
comment
@latovic, если ключ 1,2,3 ……, передать ключ для удаления элемента все еще вызывает эту проблему, не так ли? поэтому я хочу знать, почему использование индекса для удаления не работает   -  person Ian Zhong    schedule 09.08.2018


Ответы (1)


Когда вы используете v-for, вашим ключом должна быть какая-то часть данных, уникальная для рассматриваемого элемента. Индекс бесполезен, потому что он не идентифицирует элемент. Просто изменив ключ на item.name, ваш пример будет работать идеально.

В вашем примере исходный массив изменялся правильно, но vue повторно использовал ранее сгенерированные экземпляры компонентов для отображения измененного массива, и эти экземпляры имели оставшееся состояние. Vue делает это из соображений производительности, и обычно это не проблема, но подчеркивает важность выбора правильной клавиши.

Ваша проблема усугублялась парой мелочей, которые вы не часто видите в природе, и которых вам, вероятно, следует избегать ... присвоения 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(`${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>

person bbsimonbb    schedule 09.08.2018
comment
Любое предложение о том, что можно использовать в качестве ключа, если нет уникального поля? - person Ashikur Rahman; 19.11.2019
comment
Вы всегда можете присвоить своему массиву уникальные ключи, если в них уже ничего нет. Ключи - это фундаментальное требование для синхронизации DOM с массивом в вашей модели. - person bbsimonbb; 20.11.2019