可视化大屏 SDK 数据结构设计:从拖拽组件到最终渲染的全链路
源码
第三方库
react-dnd
react-grid-layout
⸻
可视化大屏 SDK 数据结构设计:从拖拽组件到最终渲染的全链路
在可视化编辑器或低代码平台中,数据结构是整个系统的核心。
只有设计合理的 Schema,才能让「从物料库拖拽组件 → 编辑参数 → 渲染成视图」这条链路高效稳定地运行。
本文将结合一个 React + TypeScript + react-grid-layout 的可视化 SDK 项目,详细介绍组件数据结构、布局存储、拖拽行为流转到最终渲染的全链路实现。
⸻
- 核心数据结构概览
项目中主要有三类数据:
1. 组件物料(Material)
• 描述一个可用的组件(包括内置组件 & 用户上传的 UMD 组件)
• 不直接参与渲染,而是作为可选物料展示在物料库中
2. 布局项(LayoutItem)
• 描述某个组件在编辑区域的位置、尺寸等信息(react-grid-layout 格式)
• 是渲染的直接依据
3. 页面配置(PageConfig)
• 存储一个页面所有组件的配置(包含布局信息 + 组件参数 + 数据源)
⸻
1.1 组件物料结构
// src/types/material.ts
export interface MaterialItem {id: string; // 唯一 ID(物料库用)name: string; // 组件名称type: 'builtin' | 'custom'; // 内置 / 自定义icon?: string; // 图标地址// 内置组件component?: React.ComponentType<any>;// 自定义组件url?: string; // UMD 组件地址globalName?: string; // UMD 全局变量名version?: string; // 版本号// 配置 schema(用于生成参数表单)configSchema?: Record<string, any>;
}
设计要点
• 内置组件直接引用 React 组件
• 自定义组件仅存储 URL & globalName,由 iframe 沙箱动态加载
• configSchema 用于动态生成编辑表单,避免硬编码
⸻
1.2 布局项结构(react-grid-layout)
// src/types/layout.ts
export interface LayoutItem {i: string; // 对应 PageComponent.idx: number;y: number;w: number;h: number;static?: boolean;
}
这是 react-grid-layout 标准结构,用来描述组件在画布中的位置和大小。
⸻
1.3 页面配置结构
// src/types/page.ts
export interface PageComponent {id: string; // 唯一 IDmaterialId: string; // 对应 MaterialItem.idprops?: Record<string, any>; // 组件配置参数dataSource?: {type: 'http' | 'code'; // HTTP 接口 / 自定义代码url?: string;transformCode?: string; // 数据转换代码};
}export interface PageConfig {layout: LayoutItem[]; // 组件位置 & 尺寸components: PageComponent[]; // 组件列表
}
⸻
- 数据流:从拖拽到渲染
整体流程可以分成 4 步:
物料库(MaterialItem)
→ 拖拽生成 PageComponent & LayoutItem
→ 存储到 PageConfig
→ 渲染组件
⸻
2.1 拖拽生成布局项
当用户从物料库拖拽组件到编辑区域时:
// 伪代码function handleDrop(material: MaterialItem, position: {x: number, y: number}) {const id = nanoid();const newComponent: PageComponent = {id,materialId: material.id,props: {}, // 默认参数};const newLayout: LayoutItem = {i: id,x: position.x,y: position.y,w: 4,h: 4,};pageConfig.components.push(newComponent);pageConfig.layout.push(newLayout);
}
这里的 layout.i 和 component.id 对应绑定,保证布局与组件一一映射。
⸻
2.2 编辑组件参数
• 读取 MaterialItem.configSchema 生成表单
• 表单修改后更新 PageComponent.props
• 数据源配置也存储在 PageComponent.dataSource
// 修改 props
function updateComponentProps(id: string, newProps: Record<string, any>) {const comp = pageConfig.components.find(c => c.id === id);if (comp) {comp.props = { ...comp.props, ...newProps };}
}
⸻
2.3 渲染组件
渲染时需要:
1. 根据 layout 渲染 GridLayout 容器
2. 根据 component.materialId 找到对应物料(内置 or 自定义)
3. 内置组件直接渲染,UMD 组件用 iframe 沙箱加载
// RenderCanvas.tsx
<GridLayout layout={pageConfig.layout} cols={12} rowHeight={30}>{pageConfig.components.map(comp => {const material = materialMap[comp.materialId];if (material.type === 'builtin') {const Component = material.component!;return <Component key={comp.id} {...comp.props} />;} else {return (<IframeSandboxkey={comp.id}url={material.url!}globalName={material.globalName!}props={comp.props}/>);}})}
</GridLayout>
⸻
- 设计亮点
• 统一 ID 绑定:layout 和 component 用同一个 id 映射,更新同步
• 物料 & 实例分离:物料只描述组件信息,实例化后的配置在 PageConfig
• 支持多种数据源:接口请求 & 自定义代码都能配置
• 内置 & 自定义组件共存:内置直接渲染,自定义用沙箱加载
⸻
- 总结
通过这套 Schema:
• 拖拽、编辑、渲染有了统一的结构
• 内置 & UMD 自定义组件都能流畅接入
• 数据源配置灵活,支持接口与自定义代码