Vue Vapor mode 来了

Vue Vapor mode 来了

为什么需要 Vapor Mode

从 Vue 发展的历史来看,Vue1 并没有虚拟 DOM,在 Vue2 中引入了虚拟 DOM。

为什么要在 Vue2 中引入虚拟 DOM

如果您以前使用过 JavaScript 框架,您可能对虚拟 DOM 的概念并不陌生。它涉及创建和更新 DOM 的虚拟表示,并将其存储在内存中以与真实 DOM 同步。 由于更新虚拟 DOM 比更新实际 DOM 更快(可以减少操作真实 DOM 的次数,降低了浏览器重绘和回流的频率,提高了渲染效率。),因此框架可以相对低成本地对虚拟 DOM 进行必要的更改。 虚拟 DOM 还提供了跨平台能力,同一套代码可以运行到不同的平台上。

就 Vue 而言,其基于虚拟 DOM 的渲染系统将 <template> 部分中的代码转换为实际的 DOM 节点。 该系统还能高效地管理节点的更改,这些更改可以通过 JavaScript 函数、API 调用等动态进行。

虽然虚拟 DOM 提升了速度和性能,但是更新 DOM 仍需要遍历虚拟 DOM并比较每个节点的变化,此过程中还包括需要为虚拟 DOM 树的每部分生成新的虚拟 DOM,这可能会导致不必要的内存压力。

在 Vue3 中引入了 带编译时信息的虚拟 DOM,它引入了一些有助于解决此问题的优化概念,包括

1. 缓存静态内容

它允许 Vue 在多次重新渲染中重复使用该部分的虚拟 DOM。

vue
<script setup>
import { ref } from 'vue'

const msg = ref('Hello World!')
</script>

<template>
    <div>
        <div>foo</div> <!-- cached -->
        <div>bar</div> <!-- cached -->
        <div>{{ msg }}</div>
    </div>
</template>

我们可以看到静态的两个 div 标签被打上了 /* CACHED */ 标记,会变化的文本标签被打上了 /* TEXT */,这就引入了另一个优化点:更新类型标记

2. 更新类型标记

对于单个有动态绑定的元素来说,我们可以在编译时推断出大量信息:

vue
<!-- 仅含 class 绑定 -->
<div :class="{ active }"></div>

<!-- 仅含 id 和 value 绑定 -->
<input :id="id" :value="value">

<!-- 仅含文本子节点 -->
<div>
{{ dynamic }}
</div>

在为这些元素生成渲染函数时,Vue 在 虚拟节点创建调用中直接编码了每个元素所需的更新类型:

就像上一个例子中的 /* TEXT */,一个元素可以有多个更新类型标记,会被合并成一个数字。运行时渲染器也将会使用位运算来检查这些标记,确定相应的更新操作。 位运算检查是非常快的。通过这样的更新类型标记,Vue 能够在更新带有动态绑定的元素时做最少的操作。

对树结构进行打平

Vue 会对虚拟 DOM 树的编译的结果会被打平为一个数组,仅包含所有动态的后代节点,当这个组件需要重渲染时,只需要遍历这个打平的树而非整棵树。这也就是我们所说的树结构打平,这大大减少了我们在虚拟 DOM 协调时需要遍历的节点数量。模板中任何的静态部分都会被高效地略过。

通过这些方法,Vue 得以在使用虚拟 DOM 的情况下在部分场景性能逼近 Svelte,并大大快于 React Vue vs React vs Svelte

尽管有了这些改进的方式,虚拟 DOM 的缺陷依然存在,比如一些不必要的内存占用,树结构的 Diff。这时我们就可以看下 Vapor Mode。

它的优点包括

  1. 没有虚拟 DOM 的运行时代码,减少包的体积并提升性能
  2. 可以细粒度的对需要更新的部分进行响应式更新
  3. 可选的,和 Vue 之前的语法没有差别。只需要添加一些配置就可以获得很多的性能优化

最新的框架性能测试

而且在结合了引入的alien-signals后,可以让响应式更加快速,性能会更加逼近原生 JS。

怎么使用 Vapor Mode?

它只在使用 script setup 的单文件组件中有用,在组件的 script 中添加 vapor

vue
<script setup vapor>
// ...
</script>

除此之外,我们需要在 createApp时添加 vaporInteropPlugin

main.tsts
import { createApp, vaporInteropPlugin } from 'vue'

import App from './App.vue'

createApp(App)
    .use(vaporInteropPlugin) // enable vapor interop
    .mount('#app')

什么时候可以使用 Vapor Mode?

  1. 创建一个新的小型项目
  2. 现有应用对性能的需求很高的子页面

当然,现在并不推荐大家使用 Vapor

  1. 目前它只是 vue3.6 的 alpha 版本,还不是很稳定。
  2. 有一些功能还不能在 Vapor 中实现
  3. 一些边缘情况还没有被覆盖

使用时需要注意的地方

  1. 最好不要 Vapor Mode 和虚拟 DOM 嵌套使用
  2. 不支持 Options API(当然,现在在普通模式中也不推荐使用 Options API)
  3. 模板表达式中不支持 $slot$props 等隐式实例属性
  4. @vue:xxx 生命周期事件
  5. 自定义指令也是不同的

更多部分我们可以查看

  1. GitHub Release
  2. YouTube Vue 3.6 ALPHA is out! All you need to know 👀

特别感谢

  1. 本文的封面图来自icarusgk
  2. 对比图表来源icarusgkx
  3. 一些文章和视频参考
翻译:Vue3 组合式API 完全指南
翻译:在Vue单文件组件中使用两个script块

评论区

评论加载中...