从 0 构建动态表单引擎- Schema 设计篇
背景
设计和开发协议驱动的动态表单引擎
的第一步就是设计 JSON 协议,也是最重要的一个环节。表单领域的 Schema设计思路 目前主要分为两种:
-
基于 JSON Schema 扩展
-
自定义 Schema
JSON Schema 介绍
具体请看文档: 理解 JSON Schema
注意很多人把自定义的 JSON 协议 混淆为 JSON Schema,这是不准确的,JSON Schema 是一套标准,是用于描述和验证 JSON 数据结构的协议。
举个例子,假设我们要定义一个表示人员信息的 JSON 数据结构,包括姓名、年龄。可以使用以下 JSON Schema 来描述这个数据结构:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"name": {
"type": "string"
},
"age": {
"type": "integer",
"minimum": 0
}
},
"required": ["name", "age"]
}
对应的 JSON 可能数据为:
{
"name":"Jack",
"age":18
}
可以使用 Ajv
等框架基于JSON Schema 描述 对 JSON 数据进行校验。
那么问题来了,为什么使用JSON Schema作为表单协议?
-
JSON Schema 是现有的国际标准,可降低一定迁移和学习成本,也方便其他人员理解。
-
JSON Schema 是对数据结构进行描述,表单就是为了提交数据,且对嵌套场景友好。
-
JSON Schema 本身就是校验数据的,可对表单校验提供帮助。
使用 JSON Schema
JSON Schema 只能对表单的数据进行描述,不能描述视图,比如字符串是用input
还是textera
,以及组件的props
等都需要进行配置。
目前业内有两种做法:
-
使用单独 UISchema对视图配置进行描述
-
扩展 JSON Schema 添加自定义属性
第一种做法的代表就是 react-jsonschema-form,这种做法的好处就是能保证 json schema
的标准化和纯洁,方便迁移和接入。缺点就是两份配置维护起来比较麻烦。
{
"schema": {
"type": "object",
"properties": {
"string": {
"title": "字符串",
"type": "string"
}
}
},
"uiSchema": {
"string": {
"ui:width": "50%"
}
}
}
第二种就是就是在 JSON Schema的原有基础上扩展一些自定义属性,代表是阿里的 Formily。它统一以x-*
格式来表示扩展属性。这种做法的好处就是维护 Schema 配置比较方便。
{
"type": "string",
"title": "字符串",
"description": "这是一个字符串",
"x-component": "Input",
"x-component-props": {
"placeholder": "请输入"
}
}
自定义 Schema
这种方式相比 JSON Schema 更灵活简洁,是以UI组件的角度对表单进行表述的,对搭建友好。
[
{
"type": "input",
"model": "key",
"label": "输入框",
"defaultValue": "",
"required": true,
"description": "描述",
"props": {}
}
]
如果是平铺的情况下还好,但如果是嵌套对象或者数组的场景下,这种 Schema 描述起来就没有 JSON Schema 那么清晰了。
注意一个问题就是使用 JSON Schema 中 properties 描述的表单项是对象的形式,遍历顺序可能会有问题。现代浏览器的遍历顺序是:整数(升序)、字符串(插入顺序)、Symbol(插入顺序),大部分情况下是字符串作为 key 的情况下是没有问题的 ,formily 等框架一般也都有支持类似
order
的字段去定义渲染顺序;如果自定义 Schema 的话可以用数组存储表单项以保证渲染顺序。
总结
基于 JSON Schema 的协议是以数据的角度对表单进行描述的;而自定义 Schema 是以 UI 的角度对表单进行描述。
两种协议的本质区别就是描述的角度不同,都属于自定义 DSL,只不过基于 JSON Schema 复用了一部分字段,各有利弊。
如果场景简单的情况,没有历史包袱,其实基于 UI 描述的 Schema 就挺好。
但如果考虑到多层嵌套以及复杂联动等场景,感觉基于 JSON Schema 扩展的方式更好一些,这也是 Formily 所采用的方案,我也会采用此方案进行开发动态表单。
目前 Vue 没有特别完善的表单解决方案,虽然 Formily 很优秀也支持 Vue,但感觉还是有些复杂,所以想自己造个轮子,当然主要还是以学习和探索为目的。后面会继续讨论表单引擎的几大问题:
-
校验
-
嵌套
-
布局
-
联动
参考
转载自:https://juejin.cn/post/7300602905798639625