从“必选项”到“性能包袱”:为什么现代框架开始“抛弃”虚拟 DOM?

一、 回顾历史:虚拟 DOM 到底解决了什么痛点?

在 2013 年 React 带着虚拟 DOM (Virtual DOM) 出来之前,我们操作网页的方式是极其原始的。

1.1 被 jQuery 支配的年代

那时候我们要改一个列表,流程通常是这样的:

  1. 拿到数据。
  2. 找到对应的 DOM 节点。
  3. 手动拼接字符串或者操作 appendChild
  4. 还要小心翼翼地处理事件解绑,否则内存就泄露了。

这种模式最大的问题不是"慢",而是状态不可控。当页面逻辑复杂到一定程度,你根本不知道是哪一段脚本改了哪一个按钮。UI 和数据完全脱节,维护起来就像在代码里拆地雷。

1.2 虚拟 DOM 并不是为了追求绝对速度

这里必须纠正一个流传很久的误区:虚拟 DOM 比原生 DOM 快。

这个结论在底层逻辑上就是错的。

虚拟 DOM 本质上是一个普通的 JavaScript 对象(Plain Object)。当你修改数据时,框架会在内存里重新创建一个 JS 对象树,然后把新树和旧树对比(Diff),算出差异,最后再去调用原生 API(如 appendChild, remove, setAttribute)来更新网页。

你看,虚拟 DOM 多做了一步 JS 计算,它怎么可能比直接操作原生 DOM 更快?

1.3 虚拟 DOM 的真正功劳:性能兜底与开发范式

虚拟 DOM 解决的是两个核心问题:

  • 研发效率:它让前端开发进入了"声明式"时代。你只需要告诉框架"我要的长相是什么样",剩下的脏活累活(对比差异、局部更新)框架全包了。
  • 防止低质量代码:新手开发者如果直接操作 DOM,很可能在循环里触发频繁的回流(Reflow),导致页面卡顿。虚拟 DOM 通过内部的缓冲合并机制,强行保证了即使你乱写,性能也不会掉出及格线。

二、 既然它这么好,为什么现在要"移除"它?

技术的发展总是伴随着成本。当我们的应用从简单的管理后台变成了像飞书、Figma 这样复杂的巨型 Web 应用时,虚拟 DOM 的副作用就开始显现了。

2.1 运行时计算的"天花板"

虚拟 DOM 的核心是 Diff 算法 。无论算法怎么优化(比如 React 的 Fiber 架构),它始终避不开一个逻辑:当数据变化时,我要通过遍历树来"猜"哪里变了。

在一个有 5000 个节点的长列表里,即使你只是改了一个复选框的状态,框架依然要递归遍历这 5000 个虚拟节点,去确认其他 4999 个节点没变。这种计算开销是随着节点数量线性增长的。在主线程处理高频交互(如拖拽、输入)时,这种 Diff 耗时会导致明显的丢帧。

2.2 内存的沉重代价

虚拟 DOM 节点在内存里是很重的。除了节点类型、属性,还要存储各种 Hooks 状态、指向真实 DOM 的引用等。

对于内存受限的移动端设备,浏览器要同时维护一份真实 DOM 和一份(甚至两份)虚拟 DOM 镜像。这不仅占用了宝贵的内存空间,还会导致频繁的 GC (垃圾回收) 触发。每当 GC 扫描这些海量的小对象时,主线程就会瞬间停顿,造成页面微小的卡死。

2.3 SSR 场景下的冗余

现在的 Web 应用很看重首屏速度(LCP),通常会用服务器端渲染(SSR)。服务器生成了 HTML,发给浏览器显示。

但问题来了:为了让这个页面能交互,客户端的 JS 必须再跑一遍,在内存里构建一棵一模一样的虚拟 DOM 树,并跟真实 DOM 对接(这个过程叫 Hydration)。

既然 HTML 都已经渲染好了,为什么我还要在客户端重新算一遍 VDOM? 这就是目前 VDOM 架构在性能优化上的一个死结。

三、 现代框架的进化:从"对比"转向"精准定位"

为了解决上述问题,Svelte、Solid.js 以及 Vue 3 的 Vapor Mode 开始尝试抛弃虚拟 DOM。它们的思路非常直白:既然运行时 Diff 太慢,那我就在编译阶段搞定。

3.1 编译器变得更聪明了

以前的编译器(如 Babel)只是把 JSX 翻译成 React.createElement。现在的编译器(如 Svelte 的编译器或 Vue 的模板分析器)在编译代码时,就能通过静态分析识别出:

  • <div>姓名:{{ name }}</div> 这一行,只有 name 是动态的。
  • 旁边的 <div>公司:字节跳动</div> 是静态的,一辈子都不会变。

3.2 细粒度更新:绕过 Diff

基于这种分析,编译器直接生成了原生 JS 代码,不再生成 VDOM。

当你修改 name 这个变量时,程序内部会直接执行:

textNode.data = newName;

没有 Diff 过程,没有树的遍历。 这就是所谓的"细粒度更新"。它就像是在代码里埋了一枚枚精准的雷管,哪里数据变了,就直接爆破哪里的 DOM 属性。

3.3 内存与运行时的精简

因为不需要在运行时存储虚拟树,也不需要内置复杂的 Diff 算法包,这些框架生成的产物体积更小,运行时的内存占用也极低。这在极致性能优化的场景下,简直是降维打击。

四、 对比:三种渲染方案的底层实现逻辑

为了让大家看得更透彻,我们用一段伪代码模拟三种方案的更新过程。

方案 A:原生命令式(jQuery)

JavaScript

javascript 复制代码
// 数据变了
data.name = '张三';
// 开发者手动更新
$('#name-label').text('张三');
  • 优点:最快。
  • 缺点:当页面有 100 个地方要改,开发者会疯掉,代码没法维护。

方案 B:虚拟 DOM 模式(React/Vue2)

JavaScript

ini 复制代码
// 数据变了
state.name = '张三';
// 框架开始工作
let newVNode = render(state); // 重新生成整棵树
let patches = diff(oldVNode, newVNode); // 递归遍历对比差异
apply(realDOM, patches); // 把差异补丁打到真实 DOM 上
  • 优点:开发爽,性能有保底。
  • 缺点:数据变动越大,树越深,Diff 越累。

方案 C:编译时无虚拟 DOM 模式(Solid/Vapor)

JavaScript

kotlin 复制代码
// 编译阶段生成的代码(伪代码)
const name_updater = (val) => textNode1.data = val;

// 运行阶段数据变了
name.set('张三'); // 触发订阅函数
name_updater('张三'); // 直接定位更新,不经过对比
  • 优点:速度接近原生操作,内存极省。
  • 缺点:对编译器的依赖极强,目前动态性不如 VDOM。

五、 如何看待 VDOM 的未来?

虽然"去 VDOM 化"是目前的趋势,但作为资深前端,我们不能无脑跟风。VDOM 在未来相当长的一段时间内依然会有其独特的生态位。

5.1 跨平台场景的"万能胶水"

如果你的业务不只是 Web,还要出移动端 App(React Native)、小程序、甚至车载屏幕系统,虚拟 DOM 依然是最好的选择。

因为它本质上是一层抽象协议。它把 UI 变成了一个普通的 JS 对象。这个对象发给浏览器,浏览器能渲染成 HTML;发给 iOS,iOS 就能渲染成原生 View。这种解耦能力,目前"编译时直出真实 DOM"的方案还很难完美替代。

2.2 极致动态性的需求

有些业务需要从后台下发一份复杂的 JSON 布局,然后前端动态渲染。在这种完全依赖运行时的场景下,VDOM 的灵活性是非常强大的。

2.3 工业级平衡点:Vue 3 的策略

Vue 3 其实走了一条非常"中庸"且聪明的路。它保留了虚拟 DOM,但引入了 Patch Flags (静态标记)

它在编译模板时,会给动态节点打上标记(比如"这个节点只有 class 会变")。在运行时 Diff 的时候,它会跳过所有静态节点,直接去跳到那个有标记的节点上操作。这本质上是带了"导航"的虚拟 DOM,在灵活性和性能之间找到了一个绝佳的平衡点。

六、 总结:我们该如何准备?

前端技术的演进不是为了"推翻",而是为了"更高效地解决具体问题"。

  • 对于极致性能追求的应用 (如编辑器、大型看板、低功耗移动端):你应该关注 Solid.jsVue 的 Vapor Mode,去理解那种不需要 Diff 的精准更新思想。
  • 对于通用型业务、多端复用的项目:成熟的 VDOM 框架(React, Vue 3)依然是目前工程化最稳妥、生态最丰富的选择。

我们没必要为了"虚拟 DOM 消失了"而感到焦虑。我们要关注的是渲染效率的本质:如何用更少的 JS 计算、更小的内存消耗,去实现更流畅的用户交互。

结论很简单:

过去,我们用 VDOM 来抹平 DOM 操作的复杂性;

现在,我们用更强大的编译器来抹平 VDOM 的额外开销。

前端渲染的尽头,始终是高效、精准、简洁的真实 DOM 操作。

相关推荐
田里的水稻1 小时前
OE_ubuntu24.04如何安装中文简体拼音输入法
运维·前端·chrome
云器科技1 小时前
从“数据中台“到“数智基建“:一树药业的湖仓架构升级实践
大数据·架构·湖仓平台
wordbaby1 小时前
🚀 从零到一实战:基于 Taro 构建纯血鸿蒙 (HarmonyOS NEXT) 应用踩坑全指南
前端
慧一居士1 小时前
ESM 在前端开发中的介绍和使用指导
前端
禾味1 小时前
过程即奖励|前端转后端经验分享
前端·后端·面试
爱学习的大牛1232 小时前
GPU架构学习
学习·架构·gpu
苡~2 小时前
【openclaw+claude】手机+OpenClaw+Claude实现远程AI编程系列大纲
java·前端·人工智能·智能手机·ai编程·claude api
生成论实验室2 小时前
即事经智能:一种基于生成易算的通用智能新范式(书)
人工智能·神经网络·算法·架构·信息与通信
Ryan今天学习了吗2 小时前
前端知识体系总结-前端工程化(Webpack篇)
前端·面试·前端工程化