Vue3 编译中的优化

  编译优化 指的是编译器将模板(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 进行设置,在 初始化渲染 时具有一定的性能优势
  •   减少创建虚拟节点产生开销的性能  
  •   减少内存占用
      
      缓存内联事件处理函数
      
      不缓存内联事件函数带来的问题
      
      在模板事件处理函数中,为了一些简单的更新操作,通常会在模板中编写 内联的事件处理函数,例如:
       

      ===>  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 节点收集

你可能感兴趣的:(前端)