v-for中的key是什么作用?
- key属性主要用在Vue的虚拟DOM算法,在新旧nodes对比时辨识VNodes;
- 如果不使用key,Vue会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法;
- 使用key时,它会基于key的变化重新排列元素顺序,并且会移除/销毁key不存在的元素;
VNode
首先我们要知道什么是VNode,VNode的全称是Virtual Node,也就是虚拟节点.在vue中无论是组件还是元素,它们最终在Vue中表示出来的都是一个个VNode;VNode的本质是一个JavaScript的对象;
VNode的优势
- 可以对真实的元素节点进行抽象,抽象成VNode(虚拟节点),这样方便后续对其进行各种操作;因为对于直接操作DOM来说是有很多的限制的,比如diff、clone等等,但是使用JavaScript编程语言来操作这些或者表达非常多的逻辑,就变得非常的简单;
- 其次是方便实现跨平台,包括你可以将VNode节点渲染成任意你想要的节点
如果我们不只是一个简单的div,而是有一大堆的元素,那么它们应该会形成一个VNode Tree
没有key的更新过程如下
<ul>
<li v-for="item in array"></li>
</ul>
array:['a','b','c','d']
执行该操作
this.array.splice(2,0,'f')
- 首先会通过新旧自序列获取最小共同长度
commonLength - 对公共部分循环遍历
patch。 patch结束,再处理剩余的新旧节点。- 如果
oldLength > newLength,说明需要对旧节点进行unmount - 否则,说明有新增节点,需要进行
mount; 我们会发现上面的diff算法效率并不高,虽然a跟b经过对比后发现是一样的,无需修改.但是c和d来说它们事实上并不需要有任何的改动,但是因为我们的c被f所使用了,所有后续所有的内容都要一次进行改动,并且最后进行新增;
有key的diff算法如下
<ul>
<li v-for="item in array" :key="item"></li>
</ul>
array:['a','b','c','d']
执行该操作
this.array.splice(2,0,'f')
第一步从头开始进行while遍历、比较
- a和b是一致的会继续进行比较;
- 一旦遇到了不一致的清空,比如这里c和f因为key不一致,所以就会break跳出循环
第二步从尾部开始进行遍历、比较
第三步如果旧节点遍历完毕,但是依然有新的节点,那么就新增节点
第四步如果新的节点遍历完毕,但是依然有旧的节点,那么就移除旧节点
第五步 中间还有很多未知的或者乱序的节点
核心逻辑在于通过新旧节点的位置变化构建一个最大递增子序列(贪心算法+二分查找+回溯),最大子序列能保证通过最小的移动或者patch实现节点的复用
总结
- 在没有key的时候我们的效率是非常低效的
- 在进行插入或者重置顺序的时候,保持相同的key可以让diff算法更加的高效
非Prop的Attribute
当我们传递给一个组件某个属性,但是该属性并没有定义对应的props或者emits时,就称之为 非Prop的 Attribute;常见的包括class、style、id属性等;
Attribute继承
当组件有单个根节点时,非Prop的Attribute将自动添加到根节点的Attribute中;如果我们不希望组件的根元素继承attribute,可以在组件中设置 inheritAttrs: false;我们可以通过 $attrs来访问所有的 非props的attribute;
Transition组件的原理
当插入或删除包含在 transition 组件中的元素时,Vue 将会做以下处理:
- 自动嗅探目标元素是否应用了CSS过渡或者动画,如果有,那么在恰当的时机添加/删除 CSS类名;
- 如果 transition 组件提供了JavaScript钩子函数,这些钩子函数将在恰当的时机被调用;
- 如果没有找到JavaScript钩子并且也没有检测到CSS过渡/动画,DOM插入、删除操作将会立即执行;