尽管使用了唯一键,但 v-for 在使用数组拼接时删除了错误的元素

v-for deletes the wrong element when using array splice despite the use of a unique key

我目前正在使用 v-for 打印出 'parties' 元素的列表,其中每个 'party' 由一个 <base-card> 表示。当我在代表 'parties' 数组的 additionalInfoList 数组上使用 splice() 并在控制台上记录输出时,我看到正确的元素已从数组中删除。然而,当我观察屏幕上的输出时,Vue 设法删除了数组的最后一个元素或列表中的相邻元素。根据各种建议和研究,我使用了独特的 :key 来帮助 Vue,但它仍然给我这个致命的错误。

BaseCardList.vue

<template>
    <div>
        <ul>
        <base-card v-once
        @add-parties="updateAdditionalInfoList"
        @delete-party="deleteParty">
            <template v-slot:title>
                    <slot></slot>
                    
                </template>
        </base-card>
        <base-card
        v-for="(data) in additionalInfoListComputed" :key="JSON.stringify(data.id)"
        ref="childComponent"
        @add-parties="updateAdditionalInfoList"
        @delete-party="deleteParty">
            <!-- Wrapper for the `Parties Being Served` component-->
                <template v-slot:title>
                
                    <slot></slot>
                </template>

        </base-card>
        </ul>
        
    </div>
    
    
    
</template>
<script>
export default {
   
    data() {
        return {
            selectedComponent: 'base-card',
            additionalInfoList : [],
            clearForm: false,
            counter: 0
        }
    },
    computed : {
        additionalInfoListComputed(){
            console.log('state changed');
            return this.additionalInfoList;
        }
    },

    methods: {
        updateAdditionalInfoList(additionalInfo){
            this.additionalInfoList.push(additionalInfo);
            
            this.counter++;
            console.log('The length is'+this.additionalInfoList.length);
        },
        deleteParty(resId){
            const resIndex = this.additionalInfoList.findIndex(
                res => res.id === resId
            );
            this.additionalInfoList.splice(resIndex, 1);
            console.log('Index is '+resIndex);
            console.log(this.additionalInfoList);
        }
    }
}
</script>
<style scoped>
ul{
    align-content: left;
    padding: 0
}

</style>  

BaseCard.vue

<template>
--something
</template>    
<script>
    import { EventBus } from './bus.js';
    export default {
        emits:['add-parties','delete-party'],
        
        data() {
            return {
                additionalInfo: 
                    {
                        id: new Date().toISOString(),
                        fullName: '',
                        preAuthorize: '',
                        serviceAddress: ''
                    },
                validation: {
                    fullNameIsValid: true,
                    serviceAddressIsValid: true
                },
                hideAddButton: false,
                formIsValid: true,
                addServiceButtonText: '+ Add Service Notes (Optional)',
                serviceNotes: [],
                showServiceNotes: false,
                showDeleteButton: true,
                enteredServiceNote: '', //service notes addendum
            }
        },
        computed : {
            showServiceNotex(){
                if(!this.showServiceNotes){
                    return '+Add Service Notes (Optional)'
                }else{
                    return '- Remove Service Notes';
                }
            }
        },
        methods: {
            
            setServiceNotes(){
                this.showServiceNotes = !this.showServiceNotes;
            },
            addAnotherParty(){
                this.validateForm();
                if(!this.formIsValid){
                    return;
                }
                
                let emitObj = JSON.parse(JSON.stringify(this.additionalInfo));
                this.$emit('add-parties', emitObj); //event
                this.hideAddButton = !this.hideAddButton;
                console.log(this.hideAddButton);
            },
            deleteParty(){
                this.$emit('delete-party', this.additionalInfo.id);
            },
    
            validateForm(){
                this.formIsValid = true;
    
                if(this.additionalInfo.fullName === ''){
                    this.validation.fullNameIsValid = false;
                    this.formIsValid = false;
                }
                if(this.additionalInfo.serviceAddress === ''){
                    this.validation.serviceAddressIsValid = false;
                    this.formIsValid = false;
                }
            },
            clearValidity(input){
                this.validation[input] = true; 
            },
            clearForm(){
                this.additionalInfo.fullName = '';
                this.additionalInfo.serviceAddress = '';
                this.additionalInfo.preAuthorize = false;
            }
        },
        created(){
            console.log('created');
            console.log(this.hideAddButton);
        }
    }
    </script>

输出画面

你应该将索引传递给删除方法deleteParty

<base-card v-for="(data,index) in additionalInfoListComputed" :key="JSON.stringify(data.id)" ref="childComponent"
  @add-parties="updateAdditionalInfoList" @delete-party="deleteParty(index)">
  <!-- Wrapper for the `Parties Being Served` component-->
  <template v-slot:title>

    <slot></slot>
  </template>
</base-card>

在子组件中生成 ID 的代码可能会创建重复的 ID。在足够快的机器上,所有的 ID 都将相同:

id: new Date().toISOString()

删除它并使用真正独特的东西,比如基于 v-for 索引的东西。