1、Vue渲染数据的策略
Vue 默认按照“就地更新”的策略来更新通过 v-for 渲染的元素列表。当数据项的顺序改变时,Vue 不会随之移动 DOM 元素的顺序,而是就地更新每个元素,确保它们在原本指定的索引位置上渲染。
默认模式是高效的,但只适用于列表渲染输出的结果不依赖子组件状态或者临时 DOM 状态 (例如表单输入值) 的情况。
2、虚拟DMO的 diff 算法
diff算法采用同级比较。
1、tag 标签不一致直接新节点替换旧节点。
2、tag 标签一样。
先替换属性
对比子元素
1.新老都有子元素,采用双指针方式进行对比
sameVnode 判断tag和key完全相同为同一节点,进行节点复用
头和头相等对比
尾和尾相等对比
头和尾相等对比
sameVnode 的时候传入两个新老子节点patch(oldChild,newChild)
乱序情况 -- 上面的都不符合,先遍历旧子节点数组形成 key值映射的map对象。
然后根据新子节点数组循环 按照key值和位置关系移动以及新增节点 最后删除多余的旧子节点 如果移动旧节点同样需要patch(oldChild,newChild)
2.新的有子元素,老的没有子元素。-- 直接将子元素虚拟节点转化成真实节点插入即可。
3.新的没有子元素,老的有子元素。 -- 直接清空 innerHtml
3、无 tag 标签 -- 文本节点直接比较内容是否一致
3、为什么要用key
在没有 key 的情况下,Vue 将使用一种最小化元素移动的算法,并尽可能地就地更新/复用相同类型的元素。如果传了 key,则将根据 key 的变化顺序来重新排列元素,并且将始终移除/销毁 key 已经不存在的元素。
同一个父元素下的子元素必须具有唯一的 key。重复的 key 将会导致渲染异常。
例如:有元素 A B C D E ,当我想把元素变成 B C D E 时
没有key值时,key默认都是undefined,就会按照diff算法的就地复用来进行比较,它会把A更新成B,B更新成C,C更新成D,最后删除E
有唯一的key值时,B C D E全部复用,只删除A
明显可以看出,当没有key值时改变元素会产生许多DOM操作,而DOM操作是非常消耗性能的,尤其是当有多层嵌套时,消耗的性能可想而知。
4、什么情况下使用index作为key值会出问题
在我们实际使用中使用index作为key或者不写key值,看起来除了操作DOM更耗性能,好像没有出现什么问题。
当你的只是用来做数据展示的时候,确实是没有什么问题的,但当你的子元素包含输入文本时就会出现问题了。
DEMO
<template>
<div>
<div v-for="(item,index) in list" :key="index" style="background-color: palegoldenrod;margin: 10px;padding: 10px;">
<div>{
{item.name}}</div>
<input type="input" placeholder="请输入"/>
<span @click="handleAdd()" style="margin-right: 15px;">添加</span>
<span @click="handleDelete(index)">删除</span>
</div>
</div>
</template>
<script>
export default {
data() {
return {
list: [],
};
},
created() {
this.handleAdd();
},
methods: {
handleAdd() {
let random = Math.random() * 1000;
this.list.push({
id: random,
name: random
})
},
handleDelete(i) {
this.list.splice(i, 1)
},
}
};
</script>
<style lang="scss" scoped>
</style>
此时我生成了三条数据
当我点击删除第二条数据时,可以看到文本框的内容还是原本的第二条数据的内容
原因是虚拟DOM在比较元素的时候,因为DOM上的key等属性均未发生变化,所以其自身和内部的input均被复用了。
所以我们应该养成好习惯,不在实际开发过程中把index作为key值。