v-for 中的 key
2018-07-01 本文已影响0人
围观工程师
vue官方文档不推荐使用 index 作为 v-for 的 key,一直不明白为什么,开发中也没有碰到相关问题,今天终于在知乎上碰到了这个问题。
见vvpvvp在问题中的回答
答主提供的测试代码
html
<div id="app">
<test v-for="(item,index) in list" :key="index">
<input v-model="item.value" type="input"/> <button @click="remove(index)">delete</button>
</test>
<button @click="add()">add</button>
</div>
js
Vue.component('test', {
template: '<div><slot></slot><a @click="change">{{a}}</a></div>',
data: function() {
return {
a: "click Me!"
}
},
methods: {
change: function() {
this.a = "clicked";
}
}
});
new Vue({
el: '#app',
data: {
list: []
},
methods: {
add: function() {
this.list.push({
value: 1
});
},
remove: function(index) {
this.list.splice(index, 1);
}
}
})
问题重现
点击add * 3,点击后两个 “click Me!” 使其状态改变,点击第一个 delete
问题出在哪
看了一遍回答中的评论大致明白了原因。由于使用了 index 作为 key ,当删除第一个 test 组件后,第二个 test 组件的 key 由原来的 1 变成了 0 ,第三个 test 组件的 key 由原来的 2 变成了 1 。diff (虚拟DOM的Diff算法,见原问题第一个回答) 发现少了值为 3 的 key ,于是删除其对应的 test 组件,导致了这个问题。
我为什么没触发过这个问题
由于业务原因,我的子组件状态通常取决于父组件data的属性值,即子组件状态不是独立的,需要依赖父组件数据。这使得即使由于 index 导致“删错了”子组件,父组件的数据也会重新同步给子组件,保持子组件状态与父组件数据一致。代码大致如下:
html
<div id="app">
<test v-for="(item,index) in list" :a="item.a" :key="index" @change="change(index)">
<input v-model="item.value" type="input"/> <button @click="remove(index)">delete</button>
</test>
<button @click="add()">add</button>
</div>
js
Vue.component('test', {
template: '<div><slot></slot><a @click="change">{{a}}</a></div>',
props: ['a'],
methods: {
change: function() {
this.$emit('change')
}
}
});
new Vue({
el: '#app',
data: {
list: []
},
methods: {
add: function() {
this.list.push({
value: 1,
a: 'click Me!'
});
},
remove: function(index) {
this.list.splice(index, 1);
},
change (i) {
this.list[i].a = 'clicked'
}
}
})
为什么不推荐使用 index
上面说到,由于 index 导致“删错了”子组件,父组件的数据会重新同步给子组件,保持子组件状态与父组件数据一致。如果使用唯一 id 那么 vue 就不必多做数据同步这一步操作。