2021-05-13 😯 formily 出 vue 兼容包了 2021-03-12 阅读规范定义 2021-03-03 补充完善 2020-09-01 First
存在的意义
json含义不必多说。
举例比如描述一个人的信息,在JS、TS、Java等多语言中共享,用json来描述是绝佳表达方式。但一份信息可能有多种表达,比如对日期的值的不同表述、男女的标识。因此希望有一种协议来约束json里的内容。
这就是 JSON Schema 。举个例子,我们可以这样约定json:
{
"type": "object",
"properties": {
"first_name": {
"type": "string"
},
}
}
这样我们不难理解:
给定的值 o.first_name='3'
是符合规定的,后续通过一个 验证
的步骤,会得到校验通过的结论。
如果给定的值是数字3,验证就会失败。
产生的需求
手动书写 json-schema 是复杂的。因此会有类似的快速填充工具,用户只需要操作右侧的UI页面,会自动生成 json-schema 。:
如果是 select 选择框,设定 enum 即可:
以上截图来自 https://github.com/YMFE/json-schema-editor-visual
到这里我们已经注意到,有些字段,比如 type title enum required 是有含义的,这是在 json-schema 规范里约定的内容。
接下来我们就简单了解
简单了解 JSON Schema
案例
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Product",
"description": "A product from Acme's catalog",
"type": "object",
"properties": {
"id": {
"description": "The unique identifier for a product",
"type": "integer"
},
"name": {
"description": "Name of the product",
"type": "string"
},
"price": {
"type": "number",
"minimum": 0,
"exclusiveMinimum": true
}
},
"required": ["id", "name", "price"]
}
属性解释
// ts
const type Types = 'object'|'integer'|'string'|'numnber'
const interface Values {
description:string;
type: Types;
minium?:number; // if type=number
exclusiveMinimum: number // if type=number 值>0
items: Values; // if type=array
enum: string[]; // 值是下列之一
uniqueItems:boolean // items是否独特
}
interface Schema {
// $ref 标识引用的来源
$shcema:string;
$id?:string; // 标识,方便被引用
title:string;
description:string;
type: Types[];
properties:{
[propName:string] :Values
},
required: string[]
}
详情看 JSON Schema 官网 ,具体看 下面深入规范章节。
通过学习,再回头去看刚才的UI页面,就很容易理解了。
特殊属性
其他的各项属性都比较简单,可读性强。补充其他的关键字:
- $ref 可以引用其他 schema
- definitions 遇到schema复杂时候,创建内部结构体,然后通过 $ref引用
其实就是进行了拆分,方便复用,观察下面的示例:通过访问 items.$ref
能够获取到 definitions/xxx
,也就是能够定位指定的内容。
这个东西在 swagger 导出的json文件中会用到。方便拆分。
{
"type": "array",
"items": { "$ref": "#/definitions/positiveInteger" },
"definitions": {
"positiveInteger": {
"type": "integer",
"minimum": 0,
"exclusiveMinimum": true
}
}
}
扩展
如果校验 json schema,类似于这种api
const res = validate(schema,data)
实际中提到比较多的是ajv
是json-schema 的一个js实现 https://github.com/ajv-validator/ajv
大致的api如下:
const Ajv = require("ajv").default
const ajv = new Ajv() // options can be passed, e.g. {allErrors: true}
const schema = {
properties: {
foo: {type: "float64"},
},
}
const validate = ajv.compile(schema)
const valid = validate({foo: 1}) // true
又因为默认报错是英文,需要配合 ajv-errors
这里比较简单略过。
面向前端表单设计
场景设计
JSON Schema
对前端啥场景指导?表单中台,快速书写表单。
我司是做toB 的,表单任务很多,我们内部产出了 v-json-schema-form ,可用还不错。
如果重新设计,我想准备两个包:
- core 核心包,用来整理 json-schema
- UI 框架桥接包,对接 element antDesign
桥接包丢给第三方维护就行,新出一个组件库,就适配一下。这样提供核心能力和插件机制,能够对接未来的场景。
思路
产品1
如何设计,考虑这个产品 https://dashjoin.github.io/#/
针对不同的types设定不同的类型,提供 widget 来展示不同的组件风格:毕竟表单控件本质上还是数值的可视化体现
上图把 boolean 映射为 checkbox
产品2
目前react最佳实践 https://rjsf-team.github.io/react-jsonschema-form/
提供 jsonSchema + UISchema + formData 来实现:
- jsonSchema 描述表单结构
- UISchema 覆盖接入第三方组件样式
- formData 存储表单数值
这种思路特别好。
产品3
后来发现阿里出的
- uForm现在 改名叫Formily https://formilyjs.org/
他的思路是这样:
底层核心包,中间桥阶层、上层UI框架层。
(⊙o⊙)… 没啥好说的,比我想得远。
在 JSON-schema
基础上塞东西 x-component=input
来适配input输入框。
原来如此:
答疑:为什么用 Rxjs?因为 Rxjs 在处理异步问题上,是目前最优秀的解决方案,越是复杂的联动,使用 Rxjs 越能发挥它的最大价值。
产品4
就它了。 https://form.lljj.me/#/demo?type=Simple
深入JSON Schema 规范
JSON Schema 本身是一种跨语言的通用规范,本身也在不停迭代,比较熟知的有:
draft-x
x为数字版本,早期格式是数字迭代2020-12
日期版本,目前为日期。
可以类比 es5 es6 es2020,新功能会不断补充。本次只关注最新稳定版: 2020-12
规范分三块:
- Core 核心
- Validation 校验
- Relative JSON Pointer 相对指针
这部分信息来源 http://json-schema.org/specification-links.html
因为存在 $ref 设定远程地址,这就意味着,我们可以考虑把 time 地理位置 啥的封装起来,作为 utils提供,比如看这个 http://json-schema.org/learn/examples/geographical-location.schema.json
或者看这个 formilyjs 的总结 http://formilyjs.org/#/0yTeT0/jAU8UVSYI8