Vue.js 为什么要推出 Vapor Mode?

前言

Vapor ModeVue 3.6 推出的一个新的高效渲染模式,它实现了无虚拟DOM并大幅度提升性能。

先了解下 Vue 各版本的渲染机制:

  • Vue 1.0:直接操作真实 DOM。
  • Vue 3.6 之前:生成虚拟 DOM,更新时通过比对前后虚拟 DOM 的变化,来更新对应的真实 DOM。
  • Vue 3.6: Vapor Mode 模式,。

1、Vapor Mode 是什么?

Vapor Mode 是 Vue.js 3.6 版本的一个可选编译策略,它在模版编译阶段会生成高效的 JavaScript 代码,直接操作真实 DOM 节点,并通过细粒度的响应式效果(reactive effects)来更新它们。

Vapor Mode核心思想就是,在编译阶段就精确的拿到哪些节点时永不变化的静态节点,哪些是动态节点并与哪个数据源相绑定,这样我们就不需要 VNode 和 DOM Diff 的过程了。

2、Vapor Mode 相比于虚拟 DOM 的好处有哪些?

  • 节省虚拟 DOM 的内存开销 :虚拟 DOM 毕竟是用 JS 对象描述 DOM 并存储在内存中,Vapor Mode 可以节省这部分内存。
  • 节省 DOM Diff 的运行开销 :虚拟 DOM 在更新时需要进行 DOM Diff 比对出最小更新变化,而 Vapor Mode 也可以节省这部分比对的运行开销。
  • 更小的包大小 :它去除了虚拟 DOM 的运行时代码 Virtual DOM runtime code,自然就减少了代码体积。
  • 更高的性能:通过在 effects 中直接对真实 DOM 进行细粒度的更新,性能更高。

3、如何使用 Vapor Mode?

3.1 创建一个 vue3 + ts 的项目

bash 复制代码
pnpm create vite vue3-vapor

3.2 手动修改 vue 版本

手动将 package.json 中的 vue 版本改为最新的 3.6.0-alpha.5,然后运行 pnpm i 命令安装。

json 复制代码
{
  "dependencies": {
    "vue": "3.6.0-alpha.5"
  }
}

3.3 改造入口文件 main.ts

跟 Vue2 中组件或者指令的注册方式类似,Vapor Mode 也有两种引入方式。

方式一:通过 createVaporApp 全局引入

直接使用 createVaporApp 代替 createApp 来创建应用,这样在全局的组件都会采用 Vapor Mode 的模式,而且不会引入虚拟 DOM 的运行时代码,整体应用体积会更小。

ts 复制代码
// main.ts
import { createVaporApp } from 'vue'
import './style.css'
import App from './App.vue'

createVaporApp(App as any).use(vaporInteropPlugin).mount('#app')

方式二:通过 vaporInteropPlugin 插件注册

在入口文件 main.ts 中引入 vaporInteropPlugin 插件,然后可以在组件中按需启用 Vapor Mode

ts 复制代码
// main.ts
import { createApp, vaporInteropPlugin } from 'vue'
import './style.css'
import App from './App.vue'

createApp(App).use(vaporInteropPlugin).mount('#app')

然后我们在组件中通过在 script 标签上增加 vapor 属性,就会启用 Vapor Mode,改变这个组件的编译模式,没有加 vapor 属性的组件还是会使用虚拟 DOM。

vue 复制代码
<script setup vapor>
// ...
</script>

4、Vapor Mode 构建产物分析

导入和辅助函数的区别:

  • 虚拟 DOM 模式:会导入如 _createElementVNode_createElementBlock_toDisplayString_normalizeClass 等函数,用于创建和处理 VNode。
  • Vapor Mode 模式:会导入如 _template_renderEffect_setText_setClass_setDynamicProp 等,用于直接 DOM 创建和更新。

更新机制:

  • 虚拟 DOM 模式:在状态变化时,通过 render 函数生成新的虚拟 DOM,然后 patch 真实 DOM。
  • Vapor Mode 模式:通过 _renderEffect 包裹,每个动态部分(如类、属性、文本)独立跟踪依赖。当响应式值变化时,仅执行对应的 setter 函数(如 _setText(node, value)_setClass(node, value)),直接修改 DOM 无需 diff 过程。

下面来比较下两种模式模式编译后代码的区别。

vue 复制代码
<script setup>
import { ref } from 'vue';
const msg = ref('Hello World!');
const classes = ref('p');
const count = ref(0);
</script>

<template>
  <h1 :class="classes" @click="count++">{{ msg }}</h1>
</template>

虚拟 DOM 模式编译后的代码如下(简化版):

js 复制代码
import { toDisplayString as _toDisplayString, normalizeClass as _normalizeClass, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue";

export function render(_ctx, _cache) {
  return (_openBlock(), _createElementBlock("h1", {
    class: _normalizeClass(_ctx.classes),
    onClick: _cache[0] || (_cache[0] = () => _ctx.count++)
  }, _toDisplayString(_ctx.msg), 11 /* TEXT, CLASS, NEED_PATCH */));
}

会生成 VNode,使用补丁标志(如 11)标记需要更新的部分。

Vapor Mode 模式编译后的代码如下(简化版):

js 复制代码
import { renderEffect as _renderEffect, setText as _setText, setClass as _setClass, template as _template } from "vue/vapor";

const t0 = _template("<h1></h1>");

export function render(_ctx) {
  const n0 = t0();  // 直接创建 <h1> DOM 节点
  n0.addEventListener('click', () => _ctx.count++);  // 直接事件绑定

  _renderEffect(() => _setText(n0, _ctx.msg));  // 响应式文本更新
  _renderEffect(() => _setClass(n0, _ctx.classes));  // 响应式类更新

  return n0;
}

直接使用 document.createElement 创建节点,事件通过 addEventListener 绑定,更新通过 effects 细粒度执行。

5、Vapor Mode 使用建议

  • Vapor Mode 目前还处于 alpha 版本,不建议在生产环境下使用。
  • 在对性能敏感的组件,可以选择性启动 Vapor Mode 模式提升其性能,这是一种渐进式增强的思想。
相关推荐
oden1 小时前
AI重构10000行老代码:2周完成1个月工作量的真实复盘
前端·aigc·ai编程
小满zs1 小时前
Next.js第十二章(RSC/服务端组件/客户端组件)
前端
亿元程序员1 小时前
明明直接用就可以了,非要在Creator里面写???
前端
wadesir2 小时前
Nginx负载均衡代理协议详解(从零开始搭建高可用Web服务)
前端·nginx·负载均衡
秋氘渔2 小时前
Vue 3 组合式写法:侦听器 watch 和 watchEffect 的区别及使用技巧
前端·javascript·vue.js·watch·watcheffect
光头程序员2 小时前
vue学习笔记
vue.js·笔记·学习
想睡八个小时2 小时前
已包含的文件名 “a.vue“ 仅大小写与文件名 “A.vue“ 不同
前端·vscode
阿奇__3 小时前
element二次封装组件套餐 搜索组件 表格组件 弹窗组件
javascript·vue.js·elementui
The_era_achievs_hero3 小时前
Echarts
前端·javascript·echarts