I have two components like follows:
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>
In this demo, no matter which item you click, the last item always be deleted.
I located this problem caused by key
of v-for
, if use 1, 2, 3, 4,..
as key, this problem occurs, but use others value as key like string, it just works fine;
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'
}]
};
},
check this fiddle: demo
Does it caused by virtual DOM? It seems VUE bind key and children components as cache, when arr
changed, it just re-render components in index(1,2,3,..)'s order, if some item in arr
is deleted, the length of arr
decreasing cause the last one cannot be render.
Please someone explain this to me, Thanks!
When you use v-for, your key has to be some piece of data that is unique to the item in question. Index is no good because it doesn't identify the item. Just changing the key to item.name gets your example to work perfectly.
In your example, your source array was being modified correctly, but vue was reusing previously generated component instances to display the modified array, and these instances had left-over state. Vue does this for performance reasons and it's not normally a problem, but it does highlight the importance of choosing the right key.
Your problem was made worse by a couple of little things that you don't often see in nature, and that you should probably avoid... assigning parentData.name to childData.name in the created hook: because your children were getting reused, not recreated, the childData.name became stale. The same goes for the random number.
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>