# 什么情况下Vue使用index作为key会出问题(转)
先说结论:
在使用非文本节点的组件,且这个组件没有依赖于响应式的props
,此时使用index
作为key
,那么此时对于列表的删除操作会导致视图错乱。
# 背景
在动态删除v-for
渲染的列表的过程中,如果key
指定为index
,则可能出现渲染错乱的问题。比如,你的列表有两行,你明明删了第一行,结果却是第二行消失了。
# 解析
问题源于key
的重复,假设你的列表有两行,当你删了第一行,那么第二行的key
就变成了第一行的key
,这两个key
是相同的。基于VirtualDOM
的库会把key
相同的组件认作同一个组件,因而不会去更新视图。
也就是说,你删了第一行,本来预期的第二行变成了第一行,结果变成因为key
相同,最后第一行保持不变,反而是第二行消失了。
其实在测试样例中可以看到,每次删除的都是最后一行,因为不管你想要删除哪一条,改变后的data
的索引必然是少了最后一个,所以最后一行总是被牺牲的那个。
# 为什么我没有复现
大部分时候我们即使使用index
作为key
,不会复现这个问题,这好像与我们上述分析不符。但是事实上,“使用index作为key并且不会出问题”这种场景,其实过程是这样的:
第一步:通过修改数据删除第一行,数据变化引起vue
去更新视图,更新的过程中发现key
相同,最终第一行保持不变,反而是第二行消失。这是第一次render
。
第二步:第一行的VirtualDOM
的确没有变,但是第一行的组件的props
变了,由原来第一行的props
,变成了第二行的props
,由于props
变化,第一行的组件需要使用新的props
更新视图,最终第一行变成了第二行的样子。这是第二次render
。
也就是说,“使用index作为key并且不会出问题”这种场景,是因为render
了两次,才最终达成了视图的正确更新。
# 什么时候可以复现
如果你去删除第一行的过程中,只会触发一次render
,那么就会复现问题。也就是说,如果每一行的组件,不依赖于任何“响应式”数据,那么就不会有第二次render
,问题就能复现!
用vue2
和vue3
都测试,一样的,虽然没有用React
测试,结果应该也是一样的。代码可以见这里 (opens new window)
# 特殊情况
如果列表中的组件,是简单的文本节点,文本节点没有props
,那么它会不会复现问题呢?
答案是“不会”,Vue
的Diff
过程对文本节点有特殊处理,不管key
一不一样,都会用“新的文本节点”覆盖“旧的文本节点”。
# 结论
在使用非文本节点的组件,且这个组件没有依赖于响应式的props,那么此时对于列表的删除操作会导致视图错乱。