设计一个表单Schema需要综合考虑字段定义、布局结构、验证规则、数据绑定以及动态行为。以下是详细的设计思路和实现方案:
1. Schema结构设计
表单Schema的核心结构应包含以下部分:
根节点(Form配置)
- 表单全局属性:标题、布局方式、数据模型、提交配置等。
{
"type": "Form",
"props": {
"layout": "vertical", // 布局类型(horizontal/vertical)
"labelCol": { "span": 6 }, // 标签布局
"wrapperCol": { "span": 18 },
"initialValues": { "name": "张三" }, // 初始数据
"submit": {
"api": "/api/submit", // 提交接口
"method": "POST",
"successMessage": "提交成功"
}
},
"children": [ ... ] // 表单项列表
}
表单项(Form Items)
每个表单项需定义类型、标识符、标签、校验规则等:
{
"type": "Input",
"name": "username", // 对应数据模型的字段名
"label": "用户名",
"placeholder": "请输入",
"required": true,
"rules": [
{ "type": "minLength", "value": 4, "message": "至少4位" },
{ "type": "regex", "pattern": "^\\w+$", "message": "仅允许字母数字" }
],
"visibleIf": "!isAdmin" // 条件渲染(依赖其他字段)
}
布局容器
支持嵌套布局(如Grid、Tabs、Card):
{
"type": "Grid",
"props": { "columns": 2 }, // 两列布局
"children": [
{ "type": "Input", "name": "name", "label": "姓名" },
{ "type": "Select", "name": "gender", "label": "性别" }
]
}
2. 关键设计点
(1) 数据绑定与双向更新
- 数据模型映射:每个表单项的
name
属性对应数据对象的键,例如:// Schema中的字段: { "type": "Input", "name": "email" } // 对应数据对象: const formData = { email: "user@example.com" };
- 更新机制:通过
onChange
事件将输入值同步到数据模型。
(2) 动态校验规则
- 声明式规则:通过
rules
数组定义校验逻辑,支持组合规则:"rules": [ { "type": "required", "message": "必填" }, { "type": "custom", "validator": "checkEmailFormat", // 前端自定义函数 "message": "邮箱格式错误" } ]
- 异步校验:支持调用后端接口验证:
{ "type": "async", "api": "/api/check-username", "message": "用户名已存在" }
(3) 条件渲染与联动
- 字段显隐控制:通过
visibleIf
或hiddenIf
实现动态显示:{ "type": "Input", "name": "company", "visibleIf": "userType === 'enterprise'" }
- 动态选项:下拉框选项依赖其他字段值:
{ "type": "Select", "name": "city", "options": { "source": "api", "url": "/api/cities?province={{province}}" // 动态参数 } }
(4) 复杂布局支持
- 嵌套容器:支持Grid、Tabs、Collapse等布局组件:
{ "type": "Tabs", "children": [ { "tab": "基本信息", "children": [ ... ] }, { "tab": "高级设置", "children": [ ... ] } ] }
(5) 扩展性与自定义组件
- 自定义组件注册:允许开发者扩展表单组件库:
// 注册一个自定义评分组件 formSchema.registerComponent('Rating', RatingComponent);
- 插槽机制:在特定位置插入自定义内容(如帮助文本):
{ "type": "Input", "name": "password", "extra": { "type": "Text", "content": "至少包含大小写字母和数字" } }
3. 完整示例
{
"type": "Form",
"props": {
"layout": "vertical",
"submit": { "api": "/api/submit" }
},
"children": [
{
"type": "Grid",
"props": { "columns": 2 },
"children": [
{
"type": "Input",
"name": "firstName",
"label": "名",
"required": true
},
{
"type": "Input",
"name": "lastName",
"label": "姓",
"required": true
}
]
},
{
"type": "Select",
"name": "country",
"label": "国家",
"options": ["中国", "美国", "日本"]
},
{
"type": "Button",
"text": "提交",
"action": "submit"
}
]
}
4. 技术实现
(1) 组件解析器
递归遍历Schema,根据type
映射到真实组件:
const FormRenderer = ({ schema }) => {
const Component = componentsMap[schema.type];
return (
<Component {...schema.props}>
{schema.children?.map((child) => (
<FormRenderer key={child.name} schema={child} />
))}
</Component>
);
};
(2) 校验引擎
const validate = (schema, data) => {
const errors = {};
schema.children.forEach((item) => {
item.rules?.forEach((rule) => {
if (rule.type === 'required' && !data[item.name]) {
errors[item.name] = rule.message;
}
// 其他规则校验...
});
});
return errors;
};
5. 设计权衡
- 性能优化:避免深层嵌套导致渲染卡顿(如虚拟滚动)。
- 灵活性:通过插槽和自定义组件满足复杂需求。
- 开发体验:提供Schema可视化生成工具和调试面板。
总结
设计表单Schema的核心在于解耦UI与逻辑,通过JSON配置动态生成表单。重点需关注:
- 结构清晰:明确表单、字段、布局的分层关系。
- 扩展能力:支持自定义组件、校验规则和动态行为。
- 数据驱动:确保表单数据与UI状态实时同步。