当 Vue 使用 v-for 渲染的元素列表时,它默认使用“就地更新”的策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序,而是就地更新每个元素,并且确保它们在每个索引位置正确渲染。
这句话是什么意思呢,看一下图你就会理解:


这个默认的模式是高效的,但是只适用于不依赖子组件状态或临时 DOM 状态 (例如:表单输入值) 的列表渲染输出。
为什么这么说呢,运行一下以下代码你就会发现问题:
<div>
<input type="text" v-model="name">
<button @click="add">添加</button>
</div>
<div v-for="name in list">
<input :id="name" type="radio" name="name" :value="name" v-model="checkedNames">
<label :for="name">{{name}}</label>
</div>
<div>Checked names: {{ checkedNames }}</div>
data() {
return {
name: '',
list: ['张三', '李四', '王五'],
checkedNames: []
}
},
methods: {
add() {
this.list.unshift(this.name);
}
}


从上面我们发现,开始选中的张三,当添加一个十一后,虽然选中的值显示张三,但页面展示却选中了十一,这显然是错误的,这就是就地更新造成的错误。
当我们给代码加上key时,问题就解决了(添加时保证name的唯一性)。
<div>
<input type="text" v-model="name">
<button @click="add">添加</button>
</div>
<div v-for="name in list" :key="name">
<input :id="name" type="radio" name="name" :value="name" v-model="checkedNames">
<label :for="name">{{name}}</label>
</div>
<div>Checked names: {{ checkedNames }}</div>

key的作用主要用在 Vue 的虚拟 DOM 算法,给 Vue 一个提示,以便能跟踪每个节点的身份,从而重用和重新排序现有元素,移除 key 不存在的元素(并且根据源码可以看出,在比较新老节点首尾节点交叉进行sameVnode比对都不相同时,有key后续的比对会更快,通过map可以快速定位,不需要去遍历老节点sameVnode寻找)。


如有不正确的地方,欢迎指正。
网友评论