随着Vue3.0版本的发布,我们在使用或者对其源码进行阅读时会惊讶的发现,它又又又双叒叕变强了,尤大本人在直播中也提到新的Vue会比老的Vue有1.3到2倍的提升,它的更新机制会更加的快速敏捷。下面就抛砖引玉,给大家稍微介绍下在更新机制方面主要升级的两个点:
要从一道算法题说起:
在一个给定的数组中,找到一组递增的数值,并且长度尽可能的大。
有点比较难理解,那来看具体例子:
const arr = [10,9,2,5,3,7,101,18]
[2, 3, 7, 18]这一列数组就是arr的最长递增子序列,其实[2, 3, 7, 101]也是。 所以最长递增子序列符合三个要求:
但是我们一般会找到数值较小的那一组数列,因为他们可以增长的空间会更多。
想必读完题目,大家就会发现:”这不就是求解最长递增子序列嘛?力扣都刷吐了的题了,小菜一碟。“如果是这样的话,那这个节点移动优化也就不难理解了。是的,Vue3.0就运用了求解这个问题的基础算法来实现diff过程中的更新优化,目的就是找到顺序排列的不需要变动的节点,使更新操作变得更少,如下图的LBN三个节点。
首先解释下什么是patchFlags, patchFlag 是 complier 时的 transform 阶段解析 AST Element 打上的优化标识。并且,顾名思义 patchFlag,patch 一词表示着它会为 runtime 时的 patchVNode 提供依据,从而实现靶向更新 VNode 的效果。因此,这样一来一往,也就是耳熟能详的 Vue3 巧妙结合 runtime 与 compiler 实现靶向更新和静态提升。代码里,patchFlag 被定义为一个数字枚举类型,每一个枚举值对应的标识意义会是这样:
export const enum PatchFlags {
TEXT = 1,// 动态的文本节点
CLASS = 1 << 1, // 2 动态的 class
STYLE = 1 << 2, // 4 动态的 style
PROPS = 1 << 3, // 8 动态属性,不包括类名和样式
FULL_PROPS = 1 << 4, // 16 动态 key,当 key 变化时需要完整的 diff 算法做比较
HYDRATE_EVENTS = 1 << 5, // 32 表示带有事件监听器的节点
STABLE_FRAGMENT = 1 << 6, // 64 一个不会改变子节点顺序的 Fragment
KEYED_FRAGMENT = 1 << 7, // 128 带有 key 属性的 Fragment
UNKEYED_FRAGMENT = 1 << 8, // 256 子节点没有 key 的 Fragment
NEED_PATCH = 1 << 9, // 512
DYNAMIC_SLOTS = 1 << 10, // 动态 solt
HOISTED = -1, // 特殊标志是负整数表示永远不会用作 diff
BAIL = -2 // 一个特殊的标志,指代差异算法
}
这样定义有一个好处,就是一个节点可能是多个类型,那么只需要将多个类型的值做一次按位或就可以了。
例如:一个节点既是文本节点,又带有事件监听器
那么这个节点最终的属性值则为:
文本节点 0000 0001
按位或
事件监听 0001 0000
最终的值 0001 0001
十进制表示为33,有了这个值我们就可以在更新节点时进行相应的处理了。 在判断该节点是否是这个类型时,只需要将该节点的值33和这个类型的值做一次与运算之后看看是否跟这个值相同即可。
例如:
其中大致可以分为两类:
当 patchFlag 的值「大于」 0 时,代表所对应的元素在 patchVNode 时或 render 时是可以被优化生成或更新的。
当 patchFlag 的值「小于」 0 时,代表所对应的元素在 patchVNode 时,是需要被 full diff,即进行递归遍历 VNode tree 的比较更新过程。
总结:「Vue3.0对于不参与更新的元素,做静态标记并提示,只会被创建一次,在渲染时直接复用。」
vue和react的diff算法,都是忽略跨级比较,只做同级比较。vue diff时调动patch函数,参数是vnode和oldVnode,分别代表新旧节点。
1.vue对比节点。当节点元素相同,但是classname不同,认为是不同类型的元素,删除重建,而react认为是同类型节点,只是修改节点属性。
2.vue的列表对比,采用的是两端到中间比对的方式,而react采用的是从左到右依次对比的方式。当一个集合只是把最后一个节点移到了第一个,react会把前面的节点依次移动,而vue只会把最后一个节点移到第一个。总体上,vue的方式比较高效。