Vue3渲染机制解析:编译时优化与虚拟DOM的性能跃迁

Vue渲染机制

真实DOM和虚拟DOM

  1. 虚拟DOM是将目标所需的UI通过数据结构虚拟表现出来,保存到内存中,再将真实DOM与之保持同步
  2. 过程
    • 编译:渲染函数,即返回虚拟DOM树的函数
    • 挂载:渲染器调用渲染函数,遍历返回的虚拟DOM树,构建真实DOM树
    • 更新:当依赖发生变化,副作用重新运行,创建一个更新后的虚拟DOM树,渲染器比较地遍历新旧两颗虚拟DOM树,将其中的变化应用到真实的DOM上
  3. 虚拟DOM缺点
    • 更新算法无法预知新的虚拟DOM树,总是需要遍历整棵树、比较每个 vnode 上 props 的区别来确保正确性
    • 即使一棵树的某一部分从未改变,每次渲染时还是会创建新的vnode,给了内存很大的压力
  4. 编译时优化
    • 在Vue中,框架同时控制着编译器和运行时
    • 优化方案:带编译时信息的虚拟DOM
      • 编译器可以静态分析模板并在生成的代码中留下标记,使运行时尽可能走捷径

带编译时信息的虚拟DOM

缓存静态内容
<div>
  <div>foo</div> <!-- 需缓存 -->
  <div>bar</div> <!-- 需缓存 -->
  <div>{{ dynamic }}</div>
</div>
  1. 渲染器在首次渲染时会创建foobar这两个div的vnode缓存起来,这部分的新旧vnode是完全相同的,渲染器会完全跳过对它们的差异比对,并且在后续的重新渲染中使用缓存的vnode
  2. 但有足够多连续的静态元素时,还会把它们压缩为一个静态vnode,其中包含它们的纯HTML字符串,会直接通过innerHTML挂载
靶向更新(更新类型标记)
  1. 对于单个有动态绑定的元素,在编译时就会推断出大量信息,如元素的绑定信息、子节点信息

  2. 为有动态绑定的元素生成渲染函数时,Vue会在vnode创建调用中直接编码每个元素所需的更新类型

    createElementVNode("div", {
      class: _normalizeClass({ active: _ctx.active })
    }, null, 2 /* CLASS */)
    
    • 参数2即为一个更新类型标记patch flag
    • 一个元素可以有多个更新类型标记,会被合并成一个数字
  3. 运行时渲染器会使用位运算来检查这些标记,确定相应的更新操作

    if (vnode.patchFlag & PatchFlags.CLASS /* 2 */) {
      // 更新节点的 CSS class
    }
    
  4. Vue也为vnode的子节点标记了类型,包含多个根节点的模板被表示为一个片段fragment,大多数情况下其顺序不变,这部分信息可以提供给运行时作为一个更新类型标记
    在这里插入图片描述

树结构打平

  1. 返回的虚拟DOM树是经createElementBlock() 调用创建的

    export function render() {
      return (_openBlock(), _createElementBlock(_Fragment, null, [
        /* children */
      ], 64 /* STABLE_FRAGMENT */))
    }
    
  2. 内部结构是稳定的一个部分可被称之为一个区块,每一个块都会追踪其所有带更新类型标记的后代节点(不只是直接子节点)

  3. 编译的结果会被打平为一个数组,仅包含所有动态的后代节点

    div (block root)
    - div 带有 :id 绑定
    - div 带有 {{ bar }} 绑定
    
  4. 当这个组件需要重渲染时,只需要遍历这个打平的树而非整棵树,大大减少了我们在虚拟 DOM 协调时需要遍历的节点数量

  5. v-ifv-for 指令会创建新的区块节点,一个子区块会在父区块的动态子节点数组中被追踪,这为他们的父区块保留了一个稳定的结构

对SSR激活的影响

SSR激活:将组件在服务端直接渲染成 HTML 字符串,作为服务端响应返回给浏览器,最后在浏览器端将静态的 HTML“激活”(hydrate) 为能够交互的客户端应用

  1. 单个元素的激活可以基于相应 vnode 的更新类型标记走更快的捷径。
    返回给浏览器,最后在浏览器端将静态的 HTML“激活”(hydrate) 为能够交互的客户端应用

  2. 单个元素的激活可以基于相应 vnode 的更新类型标记走更快的捷径。

  3. 在激活时只有区块节点和其动态子节点需要被遍历,这在模板层面上实现更高效的部分激活

参考资料:
Vue渲染机制:https://cn.vuejs.org/guide/extras/rendering-mechanism
vue3早已具备抛弃虚拟DOM的能力了:https://mp.weixin.qq.com/s/jNj0JZMOFs2NXTNgnyhEfg

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值