第 29 题:Vue3 自定义渲染器(Custom Renderer)原理 ------ 为什么 Vue 能渲染到 DOM / Canvas / WebGL / 三方平台?
结构同样是你喜欢的:
核心回答 → 深度原理 → 流程图 → 实战代码示例 → 面试官追问 → 高分总结
🎯 一、核心回答(面试官最想听)
Vue3 的渲染系统是基于 "平台无关(Platform-Agnostic)" 设计的。
其本质:
- 虚拟 DOM(VNode)是平台无关的
- 平台相关的 DOM API(createElement、patchProp、insert、remove)被抽象成 hostXXX 函数
- 自定义渲染器通过
createRenderer()自己提供这些 hostAPI,就能让 Vue 渲染到任何环境。
所以:
Vue3 = 渲染框架(runtime-core) + 平台适配器(runtime-dom)
DOM、Canvas、WebGL、MiniApp 都只是"适配器不同,核心相同"。
🎯 二、深度原理(高端面试官会重点听)
1️⃣ Vue2 为什么不能自定义渲染器?
因为 Vue2 把:
- VNode 处理
- DOM 操作
写死在一起。
Vue3 把它们完全拆开:
核心(runtime-core)
- diff 算法
- vnode 结构
- 组件系统
- 更新队列
平台(runtime-dom)
- document.createElement
- el.innerHTML
- addEventListener
2️⃣ Vue3 自定义渲染器关键 API:createRenderer
源码路径:
packages/runtime-core/src/renderer.ts
核心接口:
lua
createRenderer({
createElement,
patchProp,
insert,
remove,
setElementText
})
你可以把它想象成:
Vue3 把 DOM 操作外包给你了。
你提供函数,Vue 负责调用。
3️⃣ Vue 渲染流程(可视化理解)
scss
render() → 生成 VNode → patch() → 根据 hostAPI 创建真实节点
示意:
scss
VNode: { type:'rect', props:{x:10,y:20} }
↓ patch()
hostCreateElement(type)
hostPatchProp(node, key, value)
hostInsert(node, container)
DOM 变 Canvas,只要改 hostAPI 实现:
- createElement → 创建 canvas shape
- patchProp → 更新 shape 属性
- insert → 加入 canvas 容器
- remove → 删除 shape
核心渲染逻辑完全复用。
🎯 三、实战:创建一个 Canvas 渲染器(真正能运行的示例)
下面是一个可运行的最小 Canvas 渲染器:
typescript
import { createRenderer, h } from 'vue'
const renderer = createRenderer({
createElement(type) {
return { type, x: 0, y: 0 }
},
patchProp(node, key, prev, next) {
node[key] = next
},
insert(node, parent) {
parent.children.push(node)
},
remove(node, parent) {
parent.children = parent.children.filter(n => n !== node)
},
setElementText(node, text) {
node.text = text
}
})
const { createApp } = renderer
使用:
scss
createApp({
render() {
return h('rect', { x: 10, y: 20 })
}
}).mount(canvasRoot)
这里:
- rect 不是 HTML element
- 是我们自定义的 "图形节点"
真正的绘制由你来做。
🎯 四、现实场景(面试官喜欢的)
Vue3 渲染器能用来做:
- WebGL 游戏引擎(已有 demo)
- Canvas UI 库
- 跨端框架(MiaoJS / UniApp)
- Node 后端渲染(SSR)
- 自定义 PDF、SVG 等渲染
比如:
- Vue + PixiJS(游戏)
- Vue + ThreeJS(3D)
- Vue + WeChat 小程序(需要微信适配器)
🎯 五、面试官追问 + 高分回答
❓1:Vue3 runtime-core 和 runtime-dom 的关系是什么?
高分回答:
- runtime-core:平台无关,只负责组件、vnode、diff。
- runtime-dom:平台相关,只是提供 DOM API。
Vue3 的强大源于这个分层。
❓2:React 也能做自定义渲染器吗?对比 Vue?
你可以这样答:
React 也能(react-reconciler)
但难度更高、API 更复杂。
Vue3 优势:
- 渲染器 API 更简单
- diff 更解耦
- runtime-core 更独立
- 更适合跨端
高分总结:
React = 柔性更强,但复杂
Vue3 = 适配器更易写、成本更低
❓3:为什么 createRenderer 能支持 SSR?
因为 SSR 只是换了另一套 hostAPI:
- createElement → createSSRNode
- insert → appendToHTMLString
- patchProp → 拼接 attribute 字符串
同样的 renderer + 不同 hostAPI → SSR、DOM、Canvas 通吃。
❓4:为什么 Vue3 可以同时存在 2 套 renderer?(SSR + CSR)
因为 renderer 是独立创建。
浏览器端:
scss
createRenderer(domOperations)
SSR 端:
scss
createRenderer(stringOperations)
renderer 实例独立互不干扰。
🎯 六、高分总结(背下来直接秒杀面试)
Vue3 通过 createRenderer 实现了"渲染层"和"平台层"的完全解耦。
虚拟 DOM、Diff、组件系统都是平台无关的,平台差异全部抽象为 hostAPI。
因此 Vue3 可以渲染到 DOM、Canvas、WebGL、小程序、Node SSR 等任何环境,是一个真正跨平台 UI 引擎。