可视化动态表单设计器架构实践:拖拽编排、JSON Schema 驱动与组件渲染器
全面解析低代码/零代码表单设计器架构:涵盖拖拽画布(Drag & Drop)、JSON Schema 字段协议定义、右侧属性联动配置与动态渲染引擎实现。
· 9 分钟
在企业级中后台开发中,大量的业务需求集中在问卷调查、活动报名、流程审批和动态数据录入。如果每个表单都由前端工程师手写代码,不仅效率低下,且一旦字段增减就必须走发布流程。
可视化自定义表单设计器 通过“左侧物料拖拽 -> 中间画布排版 -> 右侧属性联动 -> 生成 JSON Schema -> 动态引擎渲染”的链路,将表单生产周期缩短到分钟级。
核心架构拓扑:三栏式设计器与渲染流水线
┌────────────────────────────────────────────────────────────────────────┐
│ 表单设计态 (Design Mode) │
│ ┌──────────────┬──────────────────────────────┬──────────────────────┐ │
│ │ 左侧物料库 │ 中间可视化拖拽画布 │ 右侧属性配置面板 │ │
│ │ (Components) │ (Drop Canvas & Visual Tree) │ (Property Inspector) │ │
│ │ • 输入框 │ ┌──────────────────────────┐ │ • 字段名 (name) │ │
│ │ • 下拉选择 │ │ [当前激活组件: 用户名] │ │ • 标签 (label) │ │
│ │ • 日期选择 │ └──────────────────────────┘ │ • 校验规则 (rules) │ │
│ └──────────────┴──────────────────────────────┴──────────────────────┘ │
└──────────────────────────────────┬─────────────────────────────────────┘
│ 导出 JSON Schema 协议
▼
┌────────────────────────────────────────────────────────────────────────┐
│ 表单运行态 (Runtime Renderer) │
│ 根据 JSON Schema 动态遍历渲染真实表单,并负责数据双向绑定与提交校验 │
└────────────────────────────────────────────────────────────────────────┘核心数据协议:JSON Schema 设计
标准化、可序列化的 Schema 是设计态与运行态通信的桥梁:
// types/form-schema.ts
export type FieldType = 'input' | 'select' | 'textarea' | 'switch' | 'datepicker';
export interface FormFieldSchema {
id: string; // 唯一标识符
name: string; // 提交数据字段名
label: string; // 页面显示标签
type: FieldType; // 组件类型
placeholder?: string;
defaultValue?: any;
required?: boolean;
options?: Array<{ label: string; value: string | number }>; // 针对 select / radio
validationRules?: Array<{
pattern?: string;
message: string;
}>;
}
export interface FormSchema {
title: string;
layout: 'vertical' | 'horizontal';
fields: FormFieldSchema[];
}动态渲染器核心实现(Dynamic Form Renderer)
在运行态,渲染引擎根据 Schema 中的 type 从组件注册表中查找对应物料,完成状态驱动渲染:
// components/FormRenderer.tsx
'use client';
import React from 'react';
import { useForm, Controller } from 'react-hook-form';
import type { FormSchema, FormFieldSchema } from '@/types/form-schema';
// 1. 组件注册表映射
const COMPONENT_MAP: Record<string, React.FC<any>> = {
input: ({ field, schema }) => (
<input
{...field}
placeholder={schema.placeholder}
className="w-full px-3 py-2 border rounded-md"
/>
),
textarea: ({ field, schema }) => (
<textarea
{...field}
placeholder={schema.placeholder}
className="w-full px-3 py-2 border rounded-md min-h-[80px]"
/>
),
select: ({ field, schema }) => (
<select {...field} className="w-full px-3 py-2 border rounded-md">
<option value="">请选择</option>
{schema.options?.map((opt: any) => (
<option key={opt.value} value={opt.value}>
{opt.label}
</option>
))}
</select>
),
};
export function DynamicFormRenderer({ schema, onSubmit }: { schema: FormSchema; onSubmit: (data: any) => void }) {
const { control, handleSubmit, formState: { errors } } = useForm();
return (
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4 max-w-lg">
<h2 className="text-xl font-bold mb-4">{schema.title}</h2>
{schema.fields.map((fieldSchema) => {
const Component = COMPONENT_MAP[fieldSchema.type] || COMPONENT_MAP.input;
return (
<div key={fieldSchema.id} className="space-y-1">
<label className="block text-sm font-medium text-gray-700">
{fieldSchema.label} {fieldSchema.required && <span className="text-red-500">*</span>}
</label>
<Controller
name={fieldSchema.name}
control={control}
defaultValue={fieldSchema.defaultValue || ''}
rules={{ required: fieldSchema.required ? '该字段必填' : false }}
render={({ field }) => <Component field={field} schema={fieldSchema} />}
/>
{errors[fieldSchema.name] && (
<p className="text-xs text-red-500">{errors[fieldSchema.name]?.message as string}</p>
)}
</div>
);
})}
<button type="submit" className="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700">
提交表单
</button>
</form>
);
}属性联动与高级扩展
- 显隐联动(Conditional Visibility):在 Schema 中增加
visibleOn: "form.role === 'admin'",渲染器在执行前对条件表达式求值,动态挂载或注销 DOM。 - 异步选项源(Async Remote Options):支持在 Select 属性中配置
remoteUrl,组件挂载时自动发起请求拉取动态下拉字典。
总结
- 模型抽象:将表单行为完全收敛至标准的 JSON Schema。
- 动静解耦:设计态负责低代码组装,运行态轻量无依赖运行,实现真正的数据驱动 UI。