@hy-bricks/canvas
v0.1.1
Published
HyperCard 画布 — 多组件实例编排 / 自由布局 / 拖拽 / 连线 / 属性面板。受控组件,SDK 边界守住:不 fetch / 不知后端 / 不开 Drawer。
Maintainers
Readme
@hy-bricks/canvas
HyperCard 画布 — 多组件实例编排 / 渐进渲染 / 只读渲染器。受控组件,SDK 边界守住:不 fetch / 不知后端 / 不开 Drawer。
简介
@hy-bricks/canvas 是 HyperCard 画布 SDK,提供:
<HyperCardPageRenderer>— 只读页面渲染器:接受PageRenderPayload,用<RuntimeBox>渲染所有实例,支持 IntersectionObserver 渐进 mount + 视口外缓冲 disposecreateRenderScheduler— 渲染调度器 composable:管理实例级 mount/dispose 状态机,与渲染组件解耦- 协议类型 — PageDocument / PageRenderPayload / ComponentContract / ComponentVersionKey 等完整类型导出
- helper —
isDraftKey()/parseComponentVersionKey()用于区分正式版本 vs 编辑期草稿
SDK 边界铁律:
- 不 fetch — 所有数据由宿主通过 props 传入
- 不知后端 — 不依赖任何特定后端 schema / API
- 不开 Drawer / Dialog — UI 交互由宿主控制
- 不绑定 UI 框架 — 不强依赖 Element Plus / Pinia / Monaco
快速开始
pnpm add @hy-bricks/canvas @hy-bricks/core vue<script setup lang="ts">
import { HyperCardPageRenderer } from '@hy-bricks/canvas'
import type { PageRenderPayload } from '@hy-bricks/canvas'
const payload: PageRenderPayload = {
page: { id: 'p1', name: 'Demo', targets: ['pc'] },
version: { version: 'v1', label: 'v1', publishedAt: '2026-05-11T00:00:00Z' },
document: {
layout: { type: 'free', canvas: { w: 1920, h: 1080 } },
instances: [
{
instanceId: 'inst-1',
componentId: 'button',
componentVersionKey: 'button@v1',
rect: { x: 100, y: 100, w: 200, h: 60 },
zIndex: 1,
props: {},
},
],
bindings: [],
},
componentVersions: {
'button@v1': {
key: 'button@v1',
componentId: 'button',
version: 'v1',
status: 'ok',
source: {
html: '<el-button @click="say">{{ msg }}</el-button>',
js: 'export default { data(){ return { msg:"Hi" } }, methods:{ say(){ this.msg="Clicked" } } }',
css: '',
},
contract: {
propsDecl: [],
emitsDecl: [],
methodsDecl: [{ key: 'say', params: [] }],
slotsDecl: [],
modelDecl: [],
dataInputsDecl: [],
dataOutputsDecl: [],
},
},
},
}
</script>
<template>
<HyperCardPageRenderer :payload="payload" />
</template>API 概览
组件:<HyperCardPageRenderer>
只读页面渲染器。接受宿主拼好的 payload 或拆开的 document + sourceMap。
Props:
| Prop | 类型 | 默认值 | 说明 |
|---|---|---|---|
| payload | PageRenderPayload? | — | /render 端点产物(含 document + componentVersions)。优先使用 |
| document | PageDocument? | — | 页面结构(payload 为空时使用,设计器场景) |
| componentSourceMap | ComponentSourceMap? | — | 组件版本 source 字典(payload 为空时使用) |
| schedulerOptions | RenderSchedulerOptions? | — | 调度器配置(透传给 createRenderScheduler) |
行为:
- 每个
PageInstance渲染一个 wrapper div(absolute 定位)+<RuntimeBox> - 视口内:渐进 mount(每 idle frame 最多 N 个)
- 视口外:缓冲一段(默认 1500ms)后 dispose,滚回来 LRU 命中不重编
status !== 'ok'的实例渲染降级卡,不阻断整页
Composable:createRenderScheduler
渲染调度器 — 管理实例级 pending → mounting → mounted → disposing 状态机。
import { createRenderScheduler } from '@hy-bricks/canvas'
const scheduler = createRenderScheduler({
rootMargin: '500px', // 视口缓冲
disposeDelayMs: 1500, // 视口外延迟 dispose
mountConcurrency: 2, // 每帧最多挂 2 个
})
scheduler.register('inst-1', element) // 注册实例 + 开始观察
scheduler.unregister('inst-1') // 停止观察 + 清单实例 state
scheduler.isMounted('inst-1') // boolean — Vue 模板 reactive 跟踪
scheduler.getState('inst-1') // MountState — 渲染 RuntimeBox vs skeleton 判别
scheduler.dispose() // 全量清理(取消 IO + 清 timer + 清 state),onBeforeUnmount 调配置(RenderSchedulerOptions):
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| rootMargin | string | '500px' | IntersectionObserver 视口缓冲距离 |
| disposeDelayMs | number | 1500 | 视口外多久才真 dispose |
| mountConcurrency | number | 2 | 渐进 mount 每帧个数 |
| root | Element? | null(viewport) | IntersectionObserver root(画布自有滚动容器时传) |
状态(MountState):
| 状态 | 说明 |
|---|---|
| pending | 已注册,未进入视口 |
| mounting | 已进入视口,排队等 idle frame |
| mounted | 已挂载,正在渲染 |
| disposing | 已离开视口,缓冲倒计时中 |
协议类型
从 @hy-bricks/canvas 直接 import,不需要额外装包:
import type {
// 基础
Source,
// ComponentVersionKey
ComponentVersionKey,
ParsedComponentVersionKey,
// ComponentContract(7 字段)
PropDecl,
EmitDecl,
MethodDecl,
SlotDecl,
ModelDecl,
DataInputDecl,
DataOutputDecl,
ComponentContract,
ComponentKind,
// ComponentVersionAsset
ComponentVersionStatus,
ComponentVersionAsset,
DraftComponentVersionAsset,
// Page 结构
PageInstance,
PageBinding,
PageLayout,
PageDocument,
PageRenderPayload,
ComponentSourceMap,
// 版本状态 / 特性开关
CanvasVersionStatus,
CanvasFeatures,
// 组件库面板
ComponentCatalogItem,
} from '@hy-bricks/canvas'Helper
import { isDraftKey, parseComponentVersionKey } from '@hy-bricks/canvas'
isDraftKey('button@v3') // false
isDraftKey('button@draft:p1:v3') // true
parseComponentVersionKey('button@v3')
// → { kind: 'published', componentId: 'button', version: 'v3' }
parseComponentVersionKey('button@draft:p1:v3')
// → { kind: 'draft', componentId: 'button', pageId: 'p1',
// baseVersion: 'v3', baseVersionKey: 'button@v3' }
parseComponentVersionKey('garbage')
// → null性能特性
得益于 IntersectionObserver 渐进 mount + LRU 编译缓存,在数十到上百个实例的页面上仍能保持流畅:
- 首屏骨架(skeleton):payload 到达后即出占位框,不阻塞首屏
- 渐进挂载:视口内实例按 idle frame 节流挂载,避免单帧卡顿
- 滚动重用:视口外延迟 dispose + 滚回 LRU 命中,组件不重新编译
配合使用 / Ecosystem
- @hy-bricks/core — 运行时核心(canvas 的 peerDep)
- @hy-bricks/editor — 嵌入式编辑器(组件源码 IDE)
- @hy-bricks/devtools — 运行时诊断浮窗
License
MIT © hy_top
