Vue 3 自定义渲染器实战:用 createRenderer 把组件画到 Canvas
test12026-10-181 次阅读
自定义渲染器解决什么问题
Vue 默认把模板渲染成 DOM。但在一些特殊场景(Canvas、终端 UI、原生 canvas 图表、游戏)里根本没有真实 DOM 节点。Vue 3 把"渲染到什么目标"抽象成了渲染器接口,只要提供一组节点操作函数,就能让同一套组件语法渲染到任意画布。核心 API 是 createRenderer。
实现一个最小 Canvas 渲染器
下面把每个"元素"当成画布上的一个矩形,text 当成文字。我们只实现 createElement、insert、patchProp、remove、createText、setText 几个钩子即可跑起来。
// canvasRenderer.ts
import { createRenderer } from 'vue';
interface CanvasNode {
type: string;
props: Record<string, any>;
children: CanvasNode[];
parent: CanvasNode | null;
}
function draw(ctx: CanvasRenderingContext2D, node: CanvasNode, x: number, y: number) {
if (node.type === 'text') {
ctx.fillStyle = '#111';
ctx.fillText(node.props.text ?? '', x, y + 16);
return;
}
const w = node.props.width ?? 120;
const h = node.props.height ?? 40;
ctx.strokeStyle = '#3b82f6';
ctx.strokeRect(x, y, w, h);
let cy = y + 8;
for (const child of node.children) {
draw(ctx, child, x + 8, cy);
cy += 20;
}
}
export function createCanvasApp(rootComponent: any, canvas: HTMLCanvasElement) {
const ctx = canvas.getContext('2d')!;
const root: CanvasNode = { type: 'root', props: {}, children: [], parent: null };
const renderer = createRenderer<CanvasNode, CanvasNode>({
createElement: (type) => ({ type, props: {}, children: [], parent: null }),
createText: (text) => ({ type: 'text', props: { text }, children: [], parent: null }),
insert: (child, parent) => { child.parent = parent; parent.children.push(child); },
remove: (child) => {
const p = child.parent;
if (p) p.children = p.children.filter((c) => c !== child);
},
patchProp: (el, key, _old, value) => { el.props[key] = value; },
setText: (el, text) => { el.props.text = text; },
parentNode: (n) => n.parent!,
nextSibling: () => null as any,
setElementText: () => {},
});
const app = renderer.createApp(rootComponent);
app.mount(root);
// 挂载后重绘整棵节点树
ctx.clearRect(0, 0, canvas.width, canvas.height);
draw(ctx, root, 10, 10);
return app;
}
用组件语法描述画布内容
// App.vue
<template>
<box :width="200" :height="80">
<text>标题</text>
<text>副标题</text>
</box>
</template>
<script setup>
import Box from './Box.vue';
import Text from './Text.vue';
</script>
落地经验
- 自定义渲染器不是用来替代 DOM 的,它只适合"目标介质没有 DOM"的场景。
- 真实项目建议在 patchProp/insert 里节流重绘,避免每次状态变更都整树重画。
- react-three-fiber、Vue 的 native 渲染都是同一思路:把声明式组件映射到非 DOM 节点。
T
test1
文章作者
自定义渲染器解决什么问题 Vue 默认把模板渲染成 DOM。但在一些特殊场景(Canvas、终端 UI、原生 canv...
- 分类
- 技术
- 发布时间
- 2026-10-18
- 字数
- 约 2473 字
- 阅读
- 1 次
