Block Tree 是带上下文的可组合渲染块,每个节点含配置元数据、隔离状态和确定性 render 函数,支持热插拔、局部响应式更新与显式依赖注入,实现高度动态表单。

用 Block Tree 实现高度动态的表单渲染,核心不在“树”本身,而在如何让每个节点真正承载可执行逻辑、响应式状态和局部更新能力——它不是静态结构,而是运行时可装配、可热替换的渲染单元。
Block Tree 的本质:带上下文的可组合渲染块
Block Tree 不是 DOM 树的镜像,也不是 JSON Schema 的简单嵌套。它的每个节点(Block)应包含三要素:配置元数据(如 type、props)、运行时状态(非全局,可隔离)、以及一个确定性 render 函数(或能生成 VNode 的工厂)。例如一个 dynamic-input Block,其 props 可能含 valueRef(指向响应式 ref)、rules(校验函数数组)、onInput(局部事件处理器),而非直接传 raw value 或 string。
- 节点支持嵌套与扁平化共存:表单项可作为子 Block 插入 layout Block(如 grid、tabs),也可被独立 mount 到任意容器
- Block 间通信走显式依赖注入(如通过 context 或 useBlockContext),避免跨层级 props drilling
- 每个 Block 自主管理副作用(watch、effect、生命周期钩子),卸载时自动清理
动态性落地:运行时编排 + 局部响应式更新
真正的动态不靠重新 render 整棵树,而靠 Block 级别的“热插拔”与“状态局部刷新”。比如用户选择“合同类型 = 服务协议”,系统动态加载并插入 service-terms Block;切换回“采购订单”,则仅卸载该 Block 并触发其 onUnmount 清理定时器或订阅。
- 用 Block ID + schema hash 做缓存键,已加载过的 Block 直接复用实例(含当前状态),不重复初始化
- 表单值变更只触发关联 Block 的 re-render(借助 fine-grained 响应式,如 signal 或 proxy-based track/trigger)
- 条件显示逻辑写在父 Block 的 render 函数中,而非靠 v-if/v-show 控制整个子树 —— 这样隐藏的 Block 仍保有状态,只是不参与挂载
性能不打折的关键控制点
高频输入、联动校验、异步字段加载等场景下,Block Tree 容易因过度响应或无效重绘拖慢体验。关键不是减少更新,而是让每次更新更精准、更轻量。
- Block 内部使用 memoized render:对 props 和 state 做浅比较,未变则跳过 VNode 生成
- 异步 Block(如远程下拉选项)自带 loading/error/success 三种状态 Block,由同一节点驱动,不新增节点层级
- 校验不集中触发:每个 Block 自行监听自身 valueRef,在输入后 debounce 校验,并只更新自己的 error message 插槽
- 滚动区域内的长表单启用 virtualized Block list:仅渲染可视区 Block,其余保留占位但不初始化状态
实战建议:从 schema 到可运行 Block 的最小闭环
别一上来就设计通用 Block SDK。先实现一个可验证的闭环:JSON Schema → 解析为 Block 配置 → 按 type 查找注册的 Block 组件 → 注入响应式上下文 → 挂载并响应联动。过程中坚持三条线不交叉:schema 解析线(纯函数)、Block 注册线(组件映射表)、运行时调度线(blockId → instance → effect cleanup)。
- 用 Map 存 Block 类型映射,避免 if-else 链;支持 run-time register/unregister,方便 A/B 测试或灰度发布新字段类型
- 所有 Block 接收统一 context 对象,含 formId、dispatch(用于提交/重置广播)、getValues(局部取值)等,不暴露原始 store
- 调试时输出 Block Tree 快照(含各节点激活状态、依赖 ref、pending async ops),比 console.log 更直观定位卡点










