在 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-for
的key
引起的,如果使用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>
我有如下两个组件:
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-for
的key
引起的,如果使用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>