浅析Vue3.6的无虚拟dom模式Vapor Mode:vue渲染历史演进、Vapor Mode是什么及其工作原理、常见问题解析
一、Vue 的渲染历史版本

先抓住主线:Vue 一直在做同一件事,就是如何更高效地把“状态变化”变成“DOM变化”。
Vue 1.x:响应式驱动的真实 DOM 更新,走的是“数据劫持 + Watcher + 指令(v-if/v-for 等)直接操作真实 DOM”的路线,面向中小应用。
Vue 2.x:进入成熟期,核心是 Virtual DOM,通过 render -> vnode -> diff -> patch 完成更新。
Vue 3.0+:仍是 Virtual DOM,但升级为 Compiler-Informed VDOM(编译器增强版 VDOM),编译期做静态分析(比如 patch flags、静态提升),减少运行时 diff 成本。
Vue 3.6(Vapor 进入 alpha/beta 阶段):引入 Vapor Mode(可选,不是默认),尝试“少走 VDOM 中间层”,更细粒度地直接更新 DOM。
二、Vapor Mode 是什么?
Vapor Mode 是 Vue.js 3.6 版本的一个可选编译策略,它在模版编译阶段会生成高效的 JavaScript 代码,直接操作真实 DOM 节点,并通过细粒度的响应式效果(reactive effects)来更新它们。
Vapor Mode 的核心思想就是,在编译阶段就精确的拿到哪些节点时永不变化的静态节点,哪些是动态节点并与哪个数据源相绑定,这样我们就不需要 VNode 和 DOM Diff 的过程了
一句话:Vapor Mode 是 Vue 的一种新编译渲染模式,用更“直接命中”的更新方式替代传统整树 VDOM diff 路径。
用一个形象的例子来理解传统虚拟DOM模式与Vapor Mode的区别:
传统虚拟DOM模式:像是一个“建筑师”,每次接到建筑变更通知,都需要拿出新旧两份完整的建筑蓝图(VNode树),逐一比对,找出所有有差异的点,然后再生成一份施工图(Patch指令集)交给施工工人(浏览器)去执行。这个比对过程,无论建筑大小,每次都得走一遍。
Vapor Mode模式:更像是一个“超级建筑师”,在设计之初就通过精密计算,为建筑的每一个可变部分都预先生成了N份精准的“施工指令清单”。当某个部分需要变更时,无需比对蓝图,直接根据预设的指令清单,找到对应的指令并执行即可。这大大简化了运行时的工作量,实现了“哪里变化,只更新哪里”的极致精准。
你可以把它理解成:
以前:先画一份“虚拟施工图”(VNode),再对比新旧施工图,决定怎么改造房子(DOM)。
Vapor:编译时就把“哪块墙会动、怎么动”规划好,状态变了直接改对应墙面,不再每次先比整套图。
三、Vapor Mode 的工作原理
核心:传统 VDOM 的很多“运行时工作”(造 VNode、diff、patch)在浏览器里做,属于运行时负担;而 Vapor 的思路是把这些能提前算清楚的事情,尽量搬到编译阶段完成,让运行时只做最少、最精准的 DOM 更新。
工作原理按步骤:
1、深度静态分析:把模板“读透”
编译器会扫描模板,把内容分成两类:
- 静态部分:基本不会变(例如固定标签结构、固定文本、固定 class 片段等)
- 动态部分:会随数据变化(例如
{{ msg }}、:id="id"、v-if/v-for、事件绑定等)
编译器会进一步搞清楚:
- 动态部分依赖哪个响应式数据
- 动态部分最终要落到 DOM 的哪个位置(哪个节点、哪个属性、哪个文本)
这一步的意义是:把“运行时才知道要改哪里”的问题,尽量变成“编译期已经知道要改哪里”。
2、生成“直接 DOM 操作”的代码:不走 VNode 主路径
在 Vapor 模式下,模板不再主要编译成“返回 VNode 树的 render 函数”,而是编译成更接近 document.createElement、createTextNode、appendChild、textContent = setAttribute/addEventListener 这类命令式 DOM 操作。
你可以把它理解成:编译器帮你把“模板描述 UI”翻译成“浏览器能直接执行的施工动作清单”。
3、用响应式副作用(effect)把“数据 ↔ DOM 更新”钉死
对每个动态绑定,生成一个类似 renderEffect(() => { ...更新某个具体DOM点... }) 的结构,
当依赖的数据变化时,只触发对应 effect,执行那段已经编译好的 DOM 更新代码
效果是:
传统 VDOM:数据变了 -> 重新生成 VNode -> diff -> patch
Vapor:数据变了 -> 直接执行预先绑定好的更新片段
这就是:把运行时 diff 这类通用成本,换成编译期生成的专用更新路径。
4、总结:Vapor工作原理的理解
- 编译期:分析 + 生成专用更新代码(“预先写好怎么改”)
- 运行时:响应式触发最小更新(“变了就改那一点”)
总结一下:
1、传统 Virtual DOM 的思路,是运行时先造一棵“虚拟 UI 描述”Vdom,再和旧的比一比,最后把差异补到真实 DOM 上。
2、Vapor 的关键转折是:很多“比对和翻译”的工作,其实可以在构建阶段提前做完。
3、所以编译器会先对模板做深度静态分析:哪些是永远不变的静态结构,哪些会随数据变化。
4、对动态部分,编译器还会进一步搞清楚:它绑定的是哪个响应式数据,最终要改 DOM 的哪个点。
5、接下来编译器不再主要生成“返回 VNode 的 render”,而是生成更接近浏览器的直接 DOM 操作代码:创建节点、插入、改文本、改属性、绑事件。
6、这些动态更新会被包进响应式副作用里:数据一变,就只执行那段预先编译好的更新片段。
7、用 <p>{{ msg }}</p> 举例:先创建 p 和文本节点,再把 msg 的变化映射成 textContent 的更新。
8、用一句话总结:不是“每次重画整套蓝图再比对”,而是“哪里会变,早就写好怎么改”。
9、这就可以:把运行时的通用 diff 成本,尽量换成编译期生成的专用更新路径。
10、Vapor 的核心不是魔法口号,而是编译期算清楚 + 运行时最小执行,让更新更短、更直接。
四、Vapor 相比 Virtual DOM 的好处
1、好处 1:更高的更新效率(尤其高频更新场景)
为什么:省掉大量 VNode 创建、树比较、patch 调度开销
好处:状态变化直接落到关联节点,路径更短
2、好处 2:基线包体更小(在纯 Vapor 应用里)
为什么:使用 createVaporApp 时可避免引入完整 VDOM runtime 路径
好处:对首屏和小应用有价值
3、好处 3:更接近“细粒度渲染”模型
为什么:更新粒度从“组件级重渲染 + diff”往“依赖级精准更新”靠近
五、常见问题
为什么要干掉vdom?为什么Vapor Mode比Vdom 模式更快?没有Vdom层,Vue怎么解决跨平台问题?
先给结论:
(1)Vue 不是“彻底干掉 VDOM”,而是提供 第二条渲染路径(Vapor,按需使用)
(2)Vapor 快,核心是 少中间层、少对象创建、少整树比较、更新更精准
(3)跨平台能力的根本不在 VDOM,而在 runtime-core + renderer 抽象层
1、为什么要“干掉” VDOM?
更准确说:不是干掉,是在特定场景绕过它,提供vapor的按需使用。
通俗理解:VDOM 像“先画施工图(VNode),再比两版图,再决定改哪儿”,这很通用、很稳,但每次更新都要付出 “画图+比图” 的固定成本。
Vue 推 Vapor 的动机是:(1)在高频更新场景,把这层固定成本再砍掉(2)让性能上限更高、包体基线更小(纯 Vapor app)(3)跟细粒度更新模型接轨(类似 signals 思路)
2、为什么 Vapor 比 VDOM 更快?



(1)少了 VNode 创建和 diff
Vdom:更新要重新跑 render,生成新 VNode,再和旧 VNode 比较
Vapor:状态变了,直接命中绑定更新逻辑,改对应 DOM
(2)更新粒度更细
VDOM 常是“组件重渲染后再 diff”
Vapor 更像“哪个依赖变了就改哪个点”,无关区域基本不参与
(3)运行时路径更短
少了通用 diff/patch 的一部分开销,尤其在频繁小更新时收益明显
但要注意的是:Vapor 并非所有场景都必然碾压,复杂动态结构、混用生态组件时收益会受实现与边界影响
3、没有 VDOM,Vue 怎么解决跨平台?
这是最容易误解的一点:跨平台能力主要来自“渲染器抽象”,不是来自 VDOM 本身。
Vue 的结构是:(1)@vue/runtime-core:平台无关核心(组件、响应式调度、生命周期等)(2)renderer:平台相关操作(createElement、insert、patchProp...)
官方有 createRenderer(),你提供一组平台 API,就能渲染到不同宿主环境。runtime-dom 本质也是基于这套抽象实现的。
所以:有无 VDOM,只是“更新策略”不同;跨平台靠的是“核心逻辑 + Host API 适配层”分离。
VDOM:通用型,生态稳,心智统一
Vapor:性能型,路径更短,适合性能敏感区
跨平台:靠 renderer 抽象,不是靠 VDOM 才能做
这个问题其实非常典型,我用一个生活化类比 + 技术映射讲清楚。
先说结论(一句话):Vue 的跨平台能力,核心靠的是“渲染器接口(renderer)”,不是靠 VDOM。VDOM 只是“怎么计算要改什么”,而跨平台是“怎么在不同平台执行这些改动”。
(1)用“翻译官”类比理解,把 Vue 想成一个“总部”:
总部负责:业务规则、状态变化、组件生命周期、依赖追踪(这些在 runtime-core)
不同国家(平台)说不同语言:
- 浏览器:DOM API
- 小程序:小程序节点 API
- Native:原生 UI API
- Canvas:绘图 API
所以 Vue 要跨平台,只需要给每个国家配一个翻译官(renderer):
- 总部说:“创建一个按钮、插入到某处、改一下文本”
- 翻译官把它翻译成该平台能执行的具体操作
关键点:有没有 VDOM,不影响“翻译官机制”存在
(2)技术上怎么分层(最重要)
@vue/runtime-core(平台无关)这里放的是“通用大脑”:组件系统、响应式调度、生命周期、更新流程控制(包括 VDOM 模式下的 patch 主流程),它不直接操作浏览器 DOM- renderer(平台相关)这里放的是“平台手脚”:
createElement、insert、remove、patchProp、setElementText 等等,这些方法在不同平台实现不同
(3)为什么“没有 VDOM 也能跨平台”?
因为跨平台本质问题是:“同一个更新意图,如何在不同宿主环境执行?”,这个问题靠 renderer 解决,而不是靠 VDOM。
- VDOM 模式:先算出“哪里变了”,再调用 renderer 去执行
- Vapor 模式:更直接地触发更新,也一样要调用 renderer 去执行
所以 VDOM 像“中间草稿纸”,renderer 像“真正施工队”。你把草稿纸简化了(Vapor),施工队照样还能在不同平台干活。
一个极简例子:假设状态变了,需要把文本从 1 改成 2:
- 浏览器 renderer:执行
el.textContent = '2' - 小程序 renderer:调用小程序的 setData/节点更新 API
- Native renderer:调用原生 TextView.setText("2")
你看,三边都能更新,核心是“renderer 适配”,和“是否 VDOM”不是一回事。
(4)那这样的话,VDOM 的真正价值是什么?
VDOM 的价值主要是:
- 给 UI 更新提供通用抽象和一致心智
- 在复杂动态结构下有成熟稳健的 diff 机制
- 生态兼容度高
但VDOM不是跨平台的唯一前提。跨平台真正依赖的是:runtime-core 把“做什么”抽象出来,renderer 定义“怎么做”。
记忆口诀:“跨平台看 renderer,不看 VDOM。”、“VDOM 决定怎么算差异,renderer 决定怎么落地执行。”
用图总结一下流程:

当数据更新时的流转图:

一句话看图结论:(1)跨平台关键点在 renderer 这层翻译(2)runtime-core只负责“要改什么”(3)所以即便没有 VDOM(走 Vapor),只要 renderer 在,跨平台依然成立。

浙公网安备 33010602011771号