发现问题
最近在做文件多标签页编辑的功能,使用了 v-for 来加载多个已打开文件的编辑器,并且在标签上有关闭按钮可以关闭指定的已打开的文件编辑器。
父组件页面渲染如下:
<template v-for="(file, index) in visitedFiles">
<editor-component v-show="currFile.name === file.name" :key="`editor${index}`" :file="file" @close="closeFile(file)"/>
</template>
当前查看的是第二个文件,而当我选择关闭第一个文件标签时,预期是第一个文件关闭,但编辑器正确显示第二个文件的内容。但是实际操作时,关闭第一个标签,第二个文件编辑器的内容会变为关闭的那个文件。
思考
第一反应是觉得 Vue 的渲染出了问题。用 v-for 进行的 DOM 元素渲染,当删除某个标签页时,因为这个删除的标签页跟其他标签页的 DOM 对象结构相同,Vue 的 DIFF 算法认为只需要更新组件的属性而不需要更新组件的渲染从而导致显示的内容不正确。
但上面只是一个想法,还需要通过各种 search 去了解一下真正的原因。
原因
这个问题是因为 Vue v-for 的“就地复用策略”引发的。当 Vue 更新用 v-for 渲染的元素列表时,默认情况下,它使用“就地复用”策略。 如果数据项的顺序已更改,则 Vue 不会移动 DOM 元素以使其与项的顺序匹配,而是会在适当位置修补每个元素,并确保其反映应在该特定索引处呈现的内容。
解决方法
将 :key="`editor${index}`" 改为 :key="file.name"
更具体的解释:为什么 Vue 中不要用 index 作为 key?