大家好,我是作曲家种太阳,Vue3 的渲染系统(runtime)是整个框架的核心,它负责将虚拟 DOM转换为真实 DOM,并在数据变化时高效地更新视图。
让你真正搞清楚:Vue3 渲染系统不仅是什么,更是为什么!
渲染系统整体流程 可以简单理解为:
rust
模板/JSX -> 虚拟节点VNode -> 渲染器Renderer -> 真正的DOM
主要经历了两个阶段:
- VNode阶段(构建虚拟DOM)
- Render阶段(挂载和更新真实DOM)
详细来说,流程如下:
- 编写模板 / JSX / 手写 VNode(h函数)
- 创建 VNode 树(虚拟 DOM 树)
- 调用 render 渲染器,把 VNode 树渲染成真实 DOM
- 后续数据变化 -> 生成新的 VNode 树
- 新旧 VNode 树进行 Diff 算法比较
- 最小化 DOM 更新(patch 更新)
每个阶段详细拆解
1. h 函数 ------ 创建虚拟节点(VNode)
做什么? 把开发者描述的 DOM 节点信息,用 JavaScript 对象(VNode)来表达。
为什么? 直接操作 DOM 很慢,内存中用 JS 对象来描述可以快速计算、比对,提升性能。
解决了什么问题?
将结构数据化,便于管理、比对
为后续 diff 提供基础
支持灵活组合(嵌套组件、slot等)
ts
const vnode = h('div', { class: 'hello' }, 'Hello Vue3')
:
最终会生成
js
{
type: 'div',
props: { class: 'hello' },
children: 'Hello Vue3'
}
2. render 函数 ------ 挂载或更新 VNode 到真实 DOM
做什么? 把 VNode 转换成浏览器真实的 DOM 元素,并插入到页面中。
为什么?
浏览器只能认识 DOM,VNode只是JS对象,必须转成真实 DOM。
解决了什么问题?
把虚拟世界的描述映射到真实世界
提供更新入口(后续diff)
两种情况:
初次挂载(没有 oldVNode) => 直接创建真实 DOM
更新(有 oldVNode) => 调用 patch 比对后最小化更新
3. patch 函数 ------ 更新虚拟节点并高效更新真实 DOM
做什么? 新旧 VNode 树进行对比,只更新真正变化的部分。
为什么? 直接销毁旧节点、重新创建新节点,开销太大,性能差。
解决了什么问题?
极大减少 DOM 操作次数
提升更新性能
核心是 Diff 算法:
类型不同 => 替换
属性变化 => 更新属性
子节点变化 => 递归比对
4. 渲染器 Renderer ------ 统一封装挂载和更新逻辑
做什么? 把所有"挂载"、"更新"细节封装成一套可复用的渲染逻辑。
为什么?
支持不同平台(浏览器 DOM、Canvas、Weex等)
让核心渲染逻辑平台无关(抽象成 runtime-core)
解决了什么问题?
可插拔的平台适配
一套逻辑,多平台复用
整体串联理解
阶段 | 解决的问题 |
---|---|
h 函数 | 把开发者描述 => 虚拟DOM 把结构数据化,方便计算和 diff |
render 函数 | 虚拟DOM => 真实DOM 将数据真实映射到浏览器 |
patch 函数 | 更新虚拟DOM差异 最小化DOM操作,提升性能 |
渲染器 Renderer | 封装挂载和更新流程 多平台适配,平台无关核心逻辑 |
总结
Vue3 的渲染系统采用了经典的"虚拟DOM + diff 更新"模式,但又在设计上做了非常多的工程优化和性能提升。
核心思路可以总结为:
把真实DOM抽象成VNode对象,统一管理
通过diff算法,做到只更新变化的部分
通过渲染器抽象,支持多平台扩展
通过批处理异步更新,提高性能(如 nextTick)
Vue3 的渲染系统,就是一套极致优化过的"创建、更新、挂载"流程。