前端工程化

可视化动态表单设计器架构实践:拖拽编排、JSON Schema 驱动与组件渲染器

全面解析低代码/零代码表单设计器架构:涵盖拖拽画布(Drag & Drop)、JSON Schema 字段协议定义、右侧属性联动配置与动态渲染引擎实现。

· 9 分钟

在企业级中后台开发中,大量的业务需求集中在问卷调查、活动报名、流程审批和动态数据录入。如果每个表单都由前端工程师手写代码,不仅效率低下,且一旦字段增减就必须走发布流程。

可视化自定义表单设计器 通过“左侧物料拖拽 -> 中间画布排版 -> 右侧属性联动 -> 生成 JSON Schema -> 动态引擎渲染”的链路,将表单生产周期缩短到分钟级。


核心架构拓扑:三栏式设计器与渲染流水线

TEXT
┌────────────────────────────────────────────────────────────────────────┐
│ 表单设计态 (Design Mode)                                                │
│ ┌──────────────┬──────────────────────────────┬──────────────────────┐ │
│ │ 左侧物料库   │ 中间可视化拖拽画布           │ 右侧属性配置面板     │ │
│ │ (Components) │ (Drop Canvas & Visual Tree)  │ (Property Inspector) │ │
│ │ • 输入框     │ ┌──────────────────────────┐ │ • 字段名 (name)      │ │
│ │ • 下拉选择   │ │ [当前激活组件: 用户名]   │ │ • 标签 (label)       │ │
│ │ • 日期选择   │ └──────────────────────────┘ │ • 校验规则 (rules)   │ │
│ └──────────────┴──────────────────────────────┴──────────────────────┘ │
└──────────────────────────────────┬─────────────────────────────────────┘
                                   │ 导出 JSON Schema 协议

┌────────────────────────────────────────────────────────────────────────┐
│ 表单运行态 (Runtime Renderer)                                          │
│  根据 JSON Schema 动态遍历渲染真实表单,并负责数据双向绑定与提交校验   │
└────────────────────────────────────────────────────────────────────────┘

核心数据协议:JSON Schema 设计

标准化、可序列化的 Schema 是设计态与运行态通信的桥梁:

TS
// 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 从组件注册表中查找对应物料,完成状态驱动渲染:

TSX
// 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>
  );
}

属性联动与高级扩展

  1. 显隐联动(Conditional Visibility):在 Schema 中增加 visibleOn: "form.role === 'admin'",渲染器在执行前对条件表达式求值,动态挂载或注销 DOM。
  2. 异步选项源(Async Remote Options):支持在 Select 属性中配置 remoteUrl,组件挂载时自动发起请求拉取动态下拉字典。

总结

  • 模型抽象:将表单行为完全收敛至标准的 JSON Schema。
  • 动静解耦:设计态负责低代码组装,运行态轻量无依赖运行,实现真正的数据驱动 UI。