编译优化 指的是编译器将模板(template)编译为渲染函数(render)的过程中,尽可能的 提取要害信息,以达到生成最优代码的过程。上面咱们一起看看 Vue3 中的编译优化的形式。
标记动静节点
标记动静节点之后,在后续渲染器更新阶段旧能够间接基于动静节点汇合,实现对动静节点的 靶向更新 或 定向更新.
patchFlag 属性
在编译器进行编译时,如果判断以后节点是属于 动静节点,就会为这个 vnode 节点打上 patchFlag 标记,也就是增加一个 patchFlag 属性,并且 patchFlag 属性 对应的 数值 代表了以后这个 动静节点的类型,如:
- 数字 1:代表该节点是 动静 的 textContent
- 数字 2:代表该节点是 动静 的 calss 绑定
- 数字 3:代表该节点是 动静 的 style 绑定
- …
dynamicChildren 属性
dynamicChildren 属性 值对应的是一个数组,其中保留的就是带有 patchFlag 属性 的 vnode 节点,并且带有 dynamicChildren 属性 的 vnode 节点成称为 块,即 Block.
Block 节点
一个 Block 实质上也是一个 虚构 DOM 节点,只不过它比一般的虚构节点多了一个用于 存储动静子节点 的 dynamicChildren 属性.
一个 Block 不仅可能收集它的 间接动静子节点,也能收集所有 动静的子代节点,而后续渲染器的更新操作将以 Block 作为更新维度去解决.
什么样的节点会变成 Block 节点?
- 所有模板的 根节点
- 带有 v-if 指令的节点
- 带有 v-for 指令的节点
- 模板中 Frament 节点所包裹的 多根节点
其中 v-if 和 v-for 指令会导致 更新前后模板构造不稳固,不过因为 v-for 指令渲染的是一组子节点,为了更好的示意这一组子节点,就须要应用 Fragment 节点来表白 v-for 指令的渲染后果,并将其作为 Block 节点.
动态晋升
动态晋升的目标是尽可能减少更新时创立 虚构 DOM 带来的 性能开销 和 内存占用.
没有动态晋升时带来的问题
通常,在响应式数据发生变化时,渲染函数就会从新执行,并产生新的虚构 DOM 节点,显然纯动态的虚构节点齐全没有必要从新创立,这会带来肯定的性能开销.
解决方案
在编译阶段能够 将纯动态节点晋升到渲染函数内部,在渲染函数外部放弃对动态节点的援用即可,当响应式数据变动引起渲染函数从新执行时,并不会从新创立动态的虚构节点,这样旧能够防止反复创立动态节点的虚构 DOM 带来的性能开销.
值得注意的是,动态晋升是以树为单位的,毕竟不可能会为每一个小的动态节点进行动态晋升,这会导致渲染函数内部对应存储动态节点的变量增多,这也会 占用肯定的内存.
预字符串化
基于 动态晋升 能够持续采纳 预字符串化 的优化伎俩,即间接将本来须要以树为单位进行动态晋升的内容,间接转换为对应基于 DOM 操作的 字符串模式.
预字符串化的劣势如下:
- 大块的动态内容能够间接通过 innerHTML 进行设置,在 初始化渲染 时具备肯定的性能劣势
- 缩小创立虚构节点产生开销的性能
-
缩小内存占用
缓存内联事件处理函数
不缓存内联事件函数带来的问题
在模板事件处理函数中,为了一些简略的更新操作,通常会在模板中编写 内联的事件处理函数,例如:
<Comp @change="c = a + b"> ===> function render(ctx){ return h(Com, { // 内联事件处理函数 onChange: () => ctx.c = ctx.a + ctx.b}) }
显然,当 render 函数被从新执行时,都为会 Comp 组件创立一个全新的 props 对象,并且其中的 onChange 事件也是一个全新的函数,这会导致渲染器对 Comp 组件进行更新,造成额定的性能开销。
解决方案
通过为 render 渲染函数传递第二个参数 cache 数组,且这个 cache 数组是来自于组件实例的,因而能够将内联事件处理函数增加到 cache 数组中缓存起来.
当渲染函数从新执行时并创立虚构 DOM 时,优先从缓存中读取对应的事件处理函数,防止事件处理函数被从新创立,导致 Comp 组件进行无用更新.
v-once 缓存虚构 DOM
Vue.js2 和 Vue.js3 中都反对 v-once 指令,以后编译器遇到 v-once 指令时,会利用下面提到的 cache 数组来缓存渲染函数的全副或局部执行后果.
v-once 的劣势
防止组件更新时从新创立虚构 DOM 带来的性能开销,因为虚构 DOM 被缓存了,因而更新时无需从新创立
防止无用的 Diff 开销,这是因为被 v-once 标记的虚构 DOM 树会被父级 Block 节点收集