vue核心原理(持续更新)

251 阅读4分钟

v-for中的key是什么作用?

  • key属性主要用在Vue的虚拟DOM算法,在新旧nodes对比时辨识VNodes;
  • 如果不使用key,Vue会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法;
  • 使用key时,它会基于key的变化重新排列元素顺序,并且会移除/销毁key不存在的元素;

VNode

首先我们要知道什么是VNode,VNode的全称是Virtual Node,也就是虚拟节点.在vue中无论是组件还是元素,它们最终在Vue中表示出来的都是一个个VNode;VNode的本质是一个JavaScript的对象;

VNode的优势

  1. 可以对真实的元素节点进行抽象,抽象成VNode(虚拟节点),这样方便后续对其进行各种操作;因为对于直接操作DOM来说是有很多的限制的,比如diff、clone等等,但是使用JavaScript编程语言来操作这些或者表达非常多的逻辑,就变得非常的简单;
  2. 其次是方便实现跨平台,包括你可以将VNode节点渲染成任意你想要的节点 image.png

如果我们不只是一个简单的div,而是有一大堆的元素,那么它们应该会形成一个VNode Tree

image.png

没有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所使用了,所有后续所有的内容都要一次进行改动,并且最后进行新增; image.png

有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跳出循环

image.png

第二步从尾部开始进行遍历、比较

image.png 第三步如果旧节点遍历完毕,但是依然有新的节点,那么就新增节点

image.png 第四步如果新的节点遍历完毕,但是依然有旧的节点,那么就移除旧节点 image.png 第五步 中间还有很多未知的或者乱序的节点

核心逻辑在于通过新旧节点的位置变化构建一个最大递增子序列(贪心算法+二分查找+回溯),最大子序列能保证通过最小的移动或者patch实现节点的复用

image.png 总结

  1. 在没有key的时候我们的效率是非常低效的
  2. 在进行插入或者重置顺序的时候,保持相同的key可以让diff算法更加的高效

非Prop的Attribute

当我们传递给一个组件某个属性,但是该属性并没有定义对应的props或者emits时,就称之为 非Prop的 Attribute;常见的包括class、style、id属性等;

Attribute继承

当组件有单个根节点时,非Prop的Attribute将自动添加到根节点的Attribute中;如果我们不希望组件的根元素继承attribute,可以在组件中设置 inheritAttrs: false;我们可以通过 $attrs来访问所有的 非props的attribute;

Transition组件的原理

当插入或删除包含在 transition 组件中的元素时,Vue 将会做以下处理:

  1. 自动嗅探目标元素是否应用了CSS过渡或者动画,如果有,那么在恰当的时机添加/删除 CSS类名;
  2. 如果 transition 组件提供了JavaScript钩子函数,这些钩子函数将在恰当的时机被调用;
  3. 如果没有找到JavaScript钩子并且也没有检测到CSS过渡/动画,DOM插入、删除操作将会立即执行;