原因:
Vue 和 React 都是采用diff算法来对比新旧虚拟dom节点去更新节点的。
在 Vue 的 diff 函数交叉对比中,当新节点和旧节点交叉对比没有结果时,会根据新节点的 key 去对比旧节点数组中的 key,从而找到相对应的节点,如果没有找到就认为是一个新增节点,而如果没有 key,就会采用遍历方式去查找对应的旧节点。
当 Vue 正在更新使用 v-for 渲染的元素列表时,它默认使用“就地复用”的策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序,而是简单复用此处每个元素。并且确保它们在每个索引位置正确渲染。这个默认的模式是高效的,但是只适用于不依赖子组件状态或临时DOM状态(例如:表单输入值)的列表渲染输出。在没有绑定key 且遍历模板简单的情况下,会导致新旧虚拟节点对比高效,节点也会复用,而这种复用就是“就地复用”。
作用:
1.避免对节点“就地复用”。需要修改的节点位置没有变,是内容更新了,这虽然提高了复用性能,但是往往在复杂的表单中会导致状态出现错位,也不会产生过渡效果。带有 key 就不会使用“就地复用”了,在 sameNode 函数 a.key===b.key 对比中就可以避免“就地复用”的情况。
2.在有 key 的情况下增加准确性,key 相当于每个 Vnode 的唯一 id,我们可以依靠 key,更快更精确的知道 oIdVnode中 对应的 Vnode 节点。利用 key 的唯一性生成 map 对象来获取相对应的节点,比遍历方式更快。
“就地复用”的解释:
如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序,而是简单复用此处每个元素。
这句话比较难理解,看下边这个例子:
首先文本,输入框,按钮是写在一个 div 里面。
在“就地复用”策略中,点击按钮,输入框不随文本一起下移,是因为输入框没有与数据(data)绑定,所以 vuejs默认使用已经渲染的 DOM,然而文本与数据是绑定的,所以文本被重新渲染,这种处理方式在 Vue 或者 Angularjs都是默认的列表渲染策略,因为高效。
这种“就地复用”一般在“列表展示”的场景中不会出现问题,所以我的建议是:如果你的列表元素存在与用户交互的场景(比如form表单或者重新排序等),那么请你为 v-for 指令设置 key 参数,key 指向列表中每个元素的唯一值。
官方解释:
“就地复用”的模式是高效的,但是只适用于不依赖子组件状态或临时 DOM 状态(例如:表单输入值)的列表渲染输出。