虚拟DOM是一种更新策略优化手段,通过内存中轻量级JS对象树比对差异后批量最小化操作真实DOM,避免高频零散DOM操作引发的重排重绘卡顿。

虚拟DOM不是直接操作真实DOM的替代品,而是一种更新策略优化手段:它通过在内存中维护一个轻量级的JS对象树(即虚拟DOM),先比对新旧虚拟树的差异(diff),再把最小化、批量化的变更应用到真实DOM上,从而避免高频、零散、重复的DOM操作带来的性能损耗。
为什么高频DOM更新会卡顿?
浏览器对DOM操作有天然开销:每次修改元素内容、样式、结构,都可能触发重排(reflow)和重绘(repaint)。若在循环或事件监听器中频繁调用 innerHTML、appendChild 或 style.color = ...,尤其在低端设备或复杂布局下,帧率会明显下降,出现卡顿甚至丢帧。
例如,在 mousemove 中实时更新一个坐标显示元素:
- 每毫秒触发一次,就尝试设置
el.textContent = `x:${x}, y:${y}` - 实际屏幕刷新仅约60fps(16.7ms一帧),大量无效更新堆积,主线程忙于DOM操作,无法及时响应其他任务
虚拟DOM如何缓解这个问题?
核心在于延迟、聚合、最小化三个动作:
立即学习“Java免费学习笔记(深入)”;
-
延迟:不立即操作真实DOM,而是先生成新的虚拟节点(如用
{ type: 'div', props: { className: 'pos' }, children: [`x:123, y:456`] }) -
聚合:在当前任务队列空闲前(如用
requestIdleCallback或防抖+批量提交),暂存多次状态变更,合并为一次虚拟树更新 - 最小化:通过高效的diff算法(如React的双端对比、Vue的静态标记跳过),只计算出真实需要增删改的节点路径,最终仅执行必要DOM操作
这相当于把“每动一下就重画整张画布”,变成“先在草稿纸记下改动点,等准备好了再精准涂改几笔”。
不依赖框架也能实践虚拟DOM思想
虚拟DOM本质是模式,不是React专属。你可以用极简方式模拟其关键逻辑:
- 用普通JS对象描述UI结构(vnode),而非直接操作DOM
- 编写一个
render(vnode, container)函数,负责首次挂载;再写一个patch(oldVnode, newVnode)函数,只更新变化部分 - 在数据变化时,不直接改DOM,而是生成新vnode,调用
patch—— 这就已迈出虚拟DOM第一步
比如一个计数器,每次点击+1,你只需返回新vnode:{ tag: 'span', children: [`count: ${count}`] },其余比对与更新由 patch 完成,无需手动查元素、设文本。
注意:虚拟DOM不是银弹
它带来收益的同时也有成本:
- 额外的内存占用(保存两棵虚拟树)
- diff过程本身消耗CPU,对超简单场景(如单个文本更新)可能反而更慢
- 无法绕过浏览器渲染机制,最终仍要操作真实DOM——只是更聪明地操作
真正有效的优化,往往结合虚拟DOM + 合理的更新节流(如 throttle mousemove)、惰性渲染(如IntersectionObserver)、以及CSS硬件加速等协同手段。








