likes
comments
collection
share

十分钟掌握Vue3模板编译优化

作者站长头像
站长
· 阅读数 56

编译流程简析

  1. 将模板进行词法分析,转化为对应的ast树(JS描述对象,与虚拟DOM原理差不多)。
  2. 转换流程transform,对动态节点做一些标记
    • 标记动态节点(Block):指令、事件、插槽、动态属性、模板语法等,渲染时进行动态节点比对即可(靶向更新)。
    • 标记节点动态类型(patchFlag),后续更新时只更新该部分即可,减少比对内容(如文本、class)。
  3. 生成代码codegen -- 虚拟dom
  4. 经过render方法将该虚拟dom挂载到宿主元素上
  5. render时直接比对动态节点。
/**
 * 当template编译时,遇到动态节点时,会创建动态节点数组dynamicChildren
 *  后续遇到普通的动态节点,则收集进dynamicChildren
 *  当遇到可能改变结构的动态节点(v-if/v-for/v-else)等
 *      则新增block节点(载体),将该特殊节点塞进其dynamicChildren中。
 *    形成blockTree
 *    进行靶向更新
 * 
 * 当template经过编译转成vnode时
 *  渲染时虚拟节点时若遇到patchFlag、dynamicChildren等标记
 *  则进行对应的优化处理
 */
let vnode = {};
vnode.dynamicChildren = [
    dyChildren1:{}, // 动态子节点
    dyChildren2:{},
    block1:[
        dyChildren1:{},
        dyChildren2:{}
    ]
]

编译流程优化

  • 动态节点标记

    • Block & Block Tree

      • 前置:diff算法的特点是同级对比后递归遍历全量比对,性能相对不好。

      • Block收集子孙动态节点,其原理类似组件收集生命周期钩子

        • 渲染组件/元素的时候,根据根节点,创建Block对象h(Fragment)
          • 在创建其子孙虚拟节点时,若该节点有动态部分createVnode中判断patchFlag
          • 收集至该对象的dynamicChildren数组中
        • 后续比对操作diff时用,渲染不用,不用担心层级问题
          • 若新孩子有dynamicChildren,直接比对两个动态节点数组动态节点数组,只有一级,无需递归tree=>array
          • 若新孩子无,则全量比对两个children,更新即可。
      • Block Tree=>不稳定的节点序列结构

        • 如果元素属性可能会影响元素结构如:vif/v-else/v-once/v-for,则该Block可能无法准确描述新旧Block的情况,无法用于比对
        • 因此对于可能影响元素结构的,会为其新增一个Fragment并创建一个Block,可能会赋予不同的key值标识每个Block避免数量不对时,与其他动态节点成同级比较导致错误
        • 因此根元素及其子孙,可能会形成多个Block组成的树BlockTree
        • 且父亲会收集儿子的Block入dynamicChildren
        • 父亲更新时会更新其dynamicChildren中的元素
      • 后续比对操作:其父亲比对两个block时,会走全量diff无法确定block中的孩子结构,无法标记

      • 特殊情况:若v-for中的值为常量v-for="item in 3",则不为其创建Block,直接走靶向更新。

    • patchFlag标靶

      • 对于动态节点的动态部分进行标记描述,后面节点比对时,直接根据类型处理即可。
      • 与元素组合的标记同理shapeFlag,都是基于位运算符。
      • transform时标记的
  • 静态提升

    • 静态节点提取为变量优先从缓存加载:对于静态节点,vue3对将其标记并提取为一个变量,当后续重复渲染时,直接使用该变量即可,不用重复创建虚拟节点。
    • 静态属性提取为变量原理同上
    • 高重复量静态节点一次性统一创建。当多个静态节点一致,则有可能会触发批量创建虚拟节点(>20)
    • 缓存事件回调提取,防止重新创建事件,复用变量@click="()=>{}"

模板编译优化总结

  • 动态节点优化
    • 通过patchFlag标识节点动态部分,也因此可以识别所有的动态节点,将其提取出来,不用递归做节点diff。平级替换
    • 通过block收集动态节点,而其中有可能影响结构的指令,则再创建block将其稳定住,也因此形成了blockTree,实现靶向更新。
  • 静态节点优化
    • 节点提升、节点批量创建、节点属性提升、函数提升
  • 结论:
    • jsx是更加灵活的渲染语法renderh('xx',{},'xxx'))],但是这种语法没有优化。
    • template语法更加的简单,且会经过模板编译优化,但书写不灵活。
转载自:https://juejin.cn/post/7241778027876859965
评论
请登录