# 设计实现
# 表单渲染
基于组件递归的形式,逐级渲染数据,如下图:(点击放大)
递归说明
schema
ui-schema
error-schema
是基于递归逐个节点拆解渲染,这部分数据基本不会变化。formData
用户输入时会高频变化的数据,为了避免每次输入导致整棵树的渲染。 这里通过当前节点path字符串curNodePath
和根节点formDatarootFormData
解析当前值。
比如:
curNodePath
为userInfo.userName
,可以很轻松的获取或者设置当前节点的值这里提供公共方法
import { vueUtils } from '@lljj/vue-json-schema-form'; // get vueUtils.getPathVal(rootFormData, curNodePath); // set vueUtils.setPathVal(rootFormData, curNodePath, value);
# 如何校验数据
校验使用 ajv (opens new window) 校验schema,搭配 error-schema 自定义校验错误提示
# 数据是通过逐级拆分校验的
举个例子:
// 如下schema
schema = {
type: 'object',
minProperties: 2,
required: [
'firstName',
'lastName'
],
properties: {
firstName: {
type: 'string',
title: 'First name',
default: 'Jun'
},
lastName: {
type: 'string',
title: 'Last name'
}
}
};
// formData
formData = {
firstName: 'Jun'
}
进行如下拆分:
上面的schema会拆分为下面三处校验,分别是依次对属性property值和和对object自身。
# 为何如此 ?
方便将每个叶子节点的校验定义在 formItem
上,用户输入时,只校验用户操作的输入框。
好处:
可以只校验当前活动的输入框计算会更快,省去了对整个表单校验再去匹配ajv到每个formItem的步骤,同时也不会导致非活动的输入框提示出校验信息。
弊端:
有些属性是无法拆分到叶子节点上,比如 object的minProperties,array的minItems,oneOf,anyOf等
- 所以会在
object
,array
,oneOf
,anyOf
节点会加一个单独的校验,在submit
的时候触发校验,如上图object校验
# 特殊处理: required
配置
object required
会拆分到每个 property
里面去校验,通过 object field
计算每个 property
是否需要 required
// 伪代码 - required 通过父节点传递props
function render(h) {
const propertiesVnode = propertiesNameList.map(name => {
return h(
ChildField,
{
props: {
...,
required: Array.isArray(schema.required)
&& schema.required.includes(name),
}
}
);
});
}
提示
对于 array
类型,每个 item
都是 required
# 其它补充
error-schema
和 ui-schema
配置数据结构都是和 schema
保持一致,如果和formData保持一致,
会导致一些场景无法配置到,比如 anyOf
,所以选择如此。
# 设计思想
设计思想借鉴react-jsonschema-form (opens new window), 在对schema的解析索引也使用了
react-json-schema
的源码,同时也顺便解决了一些原有的小问题。在用户输入的时候可以最小程度的重新渲染,做到在绝大部分的场景都是只需要重新渲染输入内容的
Widget
组件。