低代码界面的最小任务设计
低代码界面的最小任务设计生成式 UIGenerative UI把自然语言转成界面描述但模型输出不能直接当作可信输入。若 JSON 缺字段、结构不符合约定或样式和属性未受限制渲染器就可能报错或产生意外界面。生成式 UI 可以拆成一条清晰的管道LLM非确定性输出 - JSON AST Schema受校验的中间结构 - React 组件注册表受控渲染。开始时可先实现一个范围有限的 TypeScript JSON Schema 渲染器并为组件属性设置白名单等约束和使用场景稳定后再扩展能力。1. 生成式 UI 为什么容易出现渲染失败传统低代码是“人工拖拽出确定性 JSON Schema”而 Generative UI 是“LLM 根据自然语言实时生成 JSON Schema”。如果没有校验和降级这套流程常见的问题包括结构不稳定LLM 常常混淆children与items或者把数组格式写成单对象导致前端.map()函数直接崩掉。XSS 与样式注入未受控的style、dangerouslySetInnerHTML等属性可能破坏页面布局或带来安全风险。频繁渲染流式输出如果每个 Token 都触发 JSON 解析和 React 更新主线程负担会明显增加。2. 最小可运行架构三层职责拆分要让 Generative UI 的行为可控建议把职责分开第一层Schema 校验层Zod / Valibot收紧 LLM 输出的边界任何不符合定义 Schema 的结构一律在渲染前拦截或降级。第二层组件注册表Component Registry建立 SchemacomponentName到真实 React/Vue3 高效组件的映射字典。第三层渲染沙盒Render Sandbox隔离事件回调与内联样式防范层级污染。3. 生产级 TypeScript JSON Schema 渲染引擎实现下面给出一个基于 TypeScript 和 React 的最小渲染引擎示例包含属性过滤逻辑。接入前仍应按组件库和安全策略补齐 Schema 校验。import React, { FC, ReactNode, useMemo } from react; // 1. 定义生成式 UI 的 AST 节点数据结构 export interface UISchemaNode { type: string; // 组件注册名称例如 Button | Card | Metric id: string; // 节点唯一 ID props?: Recordstring, unknown; children?: UISchemaNode[] | string; } // 2. 组件注册表接口 export type ComponentMap Recordstring, FCany; // 3. 属性安全白名单校验防止 XSS 和 CSS 层级污染 function sanitizeProps(props: Recordstring, unknown {}): Recordstring, unknown { const safeProps: Recordstring, unknown {}; // 禁止直接通过 LLM 注入定位和未授权的样式 const FORBIDDEN_KEYS [dangerouslySetInnerHTML, ref, key]; for (const [key, value] of Object.entries(props)) { if (FORBIDDEN_KEYS.includes(key)) { continue; } // 对 style 属性做强制过滤 if (key style typeof value object value ! null) { const { position, zIndex, ...restStyle } value as Recordstring, unknown; safeProps.style restStyle; // 剥离 position 和 zIndex 防污染 continue; } safeProps[key] value; } return safeProps; } interface DynamicUIRendererProps { schema: UISchemaNode; registry: ComponentMap; fallbackComponent?: FC{ error: string; node: UISchemaNode }; } // 4. 递归渲染引擎核心 export const DynamicUIRenderer: FCDynamicUIRendererProps ({ schema, registry, fallbackComponent: Fallback }) { const renderNode (node: UISchemaNode): ReactNode { // 基础校验如果没有 type直接拦截 if (!node || !node.type) { return null; } const TargetComponent registry[node.type]; // 如果注册表中找不到该组件触发优雅降级而不是抛出异常 if (!TargetComponent) { if (Fallback) { return Fallback key{node.id || Math.random()} error{未找到注册组件: ${node.type}} node{node} /; } return ( div key{node.id} style{{ border: 1px dashed #ff4d4f, padding: 8px, color: #ff4d4f }} ⚠️ 未知组件类型: {node.type} /div ); } // 过滤不安全属性 const cleanProps sanitizeProps(node.props); // 递归渲染子节点 let renderedChildren: ReactNode null; if (typeof node.children string) { renderedChildren node.children; } else if (Array.isArray(node.children)) { renderedChildren node.children.map((childNode) renderNode(childNode)); } return ( TargetComponent key{node.id} {...cleanProps} {renderedChildren} /TargetComponent ); }; return {renderNode(schema)}/; };4. 真实业务注册与流式防抖在挂载这个渲染引擎时我们需要把团队原有的基础 UI 组件如 Ant Design 或 Tailwind 组件注册进来。处理大模型流式 SSEServer-Sent Events时避免每个 Chunk 到达就同步更新 state。可使用requestAnimationFrame或短时间防抖在 Schema 片段完整后再更新具体间隔和耗时需通过目标设备上的性能记录确定。# 启动测试服务验证 Generative UI 节点渲染性能 npx vite --config vite.config.ts # 打开 Chrome DevTools 查看 Performance 面板 # 观察 Task Duration 是否平滑控制在 16.6ms 帧预算以内5. 总结生成式 UI 的关键是把模型输出当作不可信数据处理。先做好 JSON Schema 校验、属性白名单和异常降级再逐步扩展可生成的组件范围。