(深入 Vue 3 渲染系统:runtime-dom 模块的设计与职责解析)
1. 概览:runtime-dom 在整个架构中的位置
Vue 3 的架构是高度模块化的。渲染逻辑被拆分为:
- runtime-core:平台无关的渲染逻辑(patch、vnode、组件系统)
- runtime-dom:浏览器平台专属渲染器(DOM 操作、属性 patch、事件绑定等)
- @vue/compiler-dom:模板编译器(将 template 转换为 render 函数)
runtime-dom
就是连接 runtime-core
与浏览器平台的桥梁。它通过实现 DOM 专属的操作接口(如 createElement、patchProp)传入 createRenderer
,构造出浏览器专用渲染器。
换句话说,runtime-core
是发动机,runtime-dom
是轮胎。
2. createRenderer:平台无关的渲染工厂
Vue 使用工厂函数创建渲染器:
const renderer = createRenderer(rendererOptions)
这个 rendererOptions
由 runtime-dom
提供,包含:
{
createElement,
patchProp,
insert,
remove,
setElementText,
setText,
createText,
createComment,
...
}
这就是 Vue 渲染“平台可插拔”的核心。只要你提供一套符合要求的 rendererOptions,就能实现:
- 浏览器渲染(runtime-dom)
- 服务端渲染(runtime-server)
- 小程序平台(如 uni-app 自定义 runtime)
- 原生 Canvas 渲染(如 pixi.js 适配器)
3. runtime-dom 的关键模块结构
目录如下:
packages/
└── runtime-dom/
├── src/
│ ├── index.ts
│ ├── patchProp.ts
│ ├── modules/
│ │ ├── attrs.ts
│ │ ├── class.ts
│ │ ├── style.ts
│ │ ├── events.ts
│ │ └── ...(每类 patch 拆分成独立模块)
index.ts
:调用createRenderer
,导出render
和createApp
patchProp.ts
:统一 patch 接口,封装 DOM 更新逻辑modules/
:不同类型的 DOM 属性 patch 模块
4. patchProp.ts:DOM 更新策略中枢
export function patchProp(el, key, prevValue, nextValue) {
if (key === 'class') {
patchClass(el, nextValue)
} else if (key === 'style') {
patchStyle(el, prevValue, nextValue)
} else if (isEvent(key)) {
patchEvent(el, key, prevValue, nextValue)
} else if (shouldSetAsProp(el, key, nextValue)) {
patchDOMProp(el, key, nextValue)
} else {
patchAttr(el, key, nextValue)
}
}
它的作用:
- 对传入的 prop 类型进行分支处理(多分支优化!)
- 不同类型的属性交由对应模块处理
- 隐藏了大量浏览器兼容细节
这是 Vue DOM 渲染性能优化的核心点之一。
5. modules/class.ts:类名绑定优化
export function patchClass(el, value) {
if (value == null) {
el.removeAttribute('class')
} else {
el.className = value
}
}
简洁高效,避免了 DOM.classList 的频繁调用。Vue 在编译阶段就会把所有静态类名静态提升。
6. modules/style.ts:样式差量更新
export function patchStyle(el, prev, next) {
const style = el.style
if (!next) {
el.removeAttribute('style')
} else {
for (const key in next) {
style[key] = next[key]
}
if (prev) {
for (const key in prev) {
if (!next[key]) {
style[key] = ''
}
}
}
}
}
特性:
- 支持内联样式对象更新
- 做差量比较,删除旧样式项
- 兼容 null/undefined 清空
7. modules/events.ts:事件绑定优化
export function patchEvent(el, name, prevVal, nextVal) {
const invokers = el._vei || (el._vei = {})
const existing = invokers[name]
if (nextVal && existing) {
existing.value = nextVal
} else {
const eventName = name.slice(2).toLowerCase()
if (nextVal) {
const invoker = (invokers[name] = createInvoker(nextVal))
el.addEventListener(eventName, invoker)
} else if (existing) {
el.removeEventListener(eventName, existing)
invokers[name] = undefined
}
}
}
特性:
- 事件函数缓存,避免频繁解绑/重绑
- 使用 invoker 包装函数动态更新
- 高性能、高兼容性
8. 自定义平台实现参考
如果你要写一个“Vue for Canvas”:
createRenderer({
createElement: () => new Sprite(),
insert: (child, parent) => parent.addChild(child),
patchProp: (el, key, _, next) => {
el.setAttr(key, next)
},
...
})
这就是 Vue 渲染器系统的力量 —— 只要你能实现 DOM 接口,Vue 就能运行。
9. runtime-dom 还能怎么玩?
- 自定义 patchProp 扩展 web component 支持
- 封装第三方库适配层(如对 echarts 封装 DOM 属性)
- 实现一个 mini 版
runtime-dom
用于教学/调试 - 和 devtools 对接实现自定义调试工具
10. 总结
runtime-dom 是 Vue 渲染系统的“落地执行者”,它的设计核心在于:
- DOM 操作职责模块化、类型分支优化
- 与
runtime-core
解耦,实现渲染平台可插拔 - 高性能实现 patch/绑定/更新策略
- 能作为自定义平台渲染器的模板参考
深入理解 runtime-dom,不只是为了看懂源码,更是打开“自定义 Vue 渲染平台”大门的钥匙。
推荐阅读
- runtime-core createRenderer 源码
- runtime-dom patchProp 源码
- 官方说明平台无关架构
- Vue Custom Renderer 示例