2021-05-13 😯 formily 出 vue 兼容包了 2021-03-12 阅读规范定义 2021-03-03 补充完善 2020-09-01 First

低代码 low code

存在的意义

json含义不必多说。

举例比如描述一个人的信息,在JS、TS、Java等多语言中共享,用json来描述是绝佳表达方式。但一份信息可能有多种表达,比如对日期的值的不同表述、男女的标识。因此希望有一种协议来约束json里的内容。

这就是 JSON Schema 。举个例子,我们可以这样约定json:

  1. {
  2. "type": "object",
  3. "properties": {
  4. "first_name": {
  5. "type": "string"
  6. },
  7. }
  8. }

这样我们不难理解:
给定的值 o.first_name='3' 是符合规定的,后续通过一个 验证 的步骤,会得到校验通过的结论。
如果给定的值是数字3,验证就会失败。

产生的需求

手动书写 json-schema 是复杂的。因此会有类似的快速填充工具,用户只需要操作右侧的UI页面,会自动生成 json-schema 。:

image.png

如果是 select 选择框,设定 enum 即可:

image.png

以上截图来自 https://github.com/YMFE/json-schema-editor-visual

到这里我们已经注意到,有些字段,比如 type title enum required 是有含义的,这是在 json-schema 规范里约定的内容。

接下来我们就简单了解

简单了解 JSON Schema

案例

  1. {
  2. "$schema": "http://json-schema.org/draft-04/schema#",
  3. "title": "Product",
  4. "description": "A product from Acme's catalog",
  5. "type": "object",
  6. "properties": {
  7. "id": {
  8. "description": "The unique identifier for a product",
  9. "type": "integer"
  10. },
  11. "name": {
  12. "description": "Name of the product",
  13. "type": "string"
  14. },
  15. "price": {
  16. "type": "number",
  17. "minimum": 0,
  18. "exclusiveMinimum": true
  19. }
  20. },
  21. "required": ["id", "name", "price"]
  22. }

属性解释

  1. // ts
  2. const type Types = 'object'|'integer'|'string'|'numnber'
  3. const interface Values {
  4. description:string;
  5. type: Types;
  6. minium?:number; // if type=number
  7. exclusiveMinimum: number // if type=number 值>0
  8. items: Values; // if type=array
  9. enum: string[]; // 值是下列之一
  10. uniqueItems:boolean // items是否独特
  11. }
  12. interface Schema {
  13. // $ref 标识引用的来源
  14. $shcema:string;
  15. $id?:string; // 标识,方便被引用
  16. title:string;
  17. description:string;
  18. type: Types[];
  19. properties:{
  20. [propName:string] :Values
  21. },
  22. required: string[]
  23. }

详情看 JSON Schema 官网 ,具体看 下面深入规范章节。

通过学习,再回头去看刚才的UI页面,就很容易理解了。

特殊属性

其他的各项属性都比较简单,可读性强。补充其他的关键字:

  • $ref 可以引用其他 schema
  • definitions 遇到schema复杂时候,创建内部结构体,然后通过 $ref引用

其实就是进行了拆分,方便复用,观察下面的示例:通过访问 items.$ref 能够获取到 definitions/xxx ,也就是能够定位指定的内容。
这个东西在 swagger 导出的json文件中会用到。方便拆分。

  1. {
  2. "type": "array",
  3. "items": { "$ref": "#/definitions/positiveInteger" },
  4. "definitions": {
  5. "positiveInteger": {
  6. "type": "integer",
  7. "minimum": 0,
  8. "exclusiveMinimum": true
  9. }
  10. }
  11. }

扩展

如果校验 json schema,类似于这种api

  1. const res = validate(schema,data)

实际中提到比较多的是ajv 是json-schema 的一个js实现 https://github.com/ajv-validator/ajv

大致的api如下:

  1. const Ajv = require("ajv").default
  2. const ajv = new Ajv() // options can be passed, e.g. {allErrors: true}
  3. const schema = {
  4. properties: {
  5. foo: {type: "float64"},
  6. },
  7. }
  8. const validate = ajv.compile(schema)
  9. const valid = validate({foo: 1}) // true

又因为默认报错是英文,需要配合 ajv-errors
这里比较简单略过。

面向前端表单设计

场景设计

JSON Schema 对前端啥场景指导?表单中台,快速书写表单。

我司是做toB 的,表单任务很多,我们内部产出了 v-json-schema-form ,可用还不错。

如果重新设计,我想准备两个包:

  • core 核心包,用来整理 json-schema
  • UI 框架桥接包,对接 element antDesign

桥接包丢给第三方维护就行,新出一个组件库,就适配一下。这样提供核心能力和插件机制,能够对接未来的场景。

从JSON Schema到前端表单 - 图3

思路

产品1

如何设计,考虑这个产品 https://dashjoin.github.io/#/

针对不同的types设定不同的类型,提供 widget 来展示不同的组件风格:毕竟表单控件本质上还是数值的可视化体现

image.png
上图把 boolean 映射为 checkbox

产品2

目前react最佳实践 https://rjsf-team.github.io/react-jsonschema-form/

image.png

提供 jsonSchema + UISchema + formData 来实现:

  • jsonSchema 描述表单结构
  • UISchema 覆盖接入第三方组件样式
  • formData 存储表单数值

这种思路特别好。

产品3

后来发现阿里出的

他的思路是这样:
image.png

底层核心包,中间桥阶层、上层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

从JSON Schema到前端表单 - 图7
因为存在 $ref 设定远程地址,这就意味着,我们可以考虑把 time 地理位置 啥的封装起来,作为 utils提供,比如看这个 http://json-schema.org/learn/examples/geographical-location.schema.json

或者看这个 formilyjs 的总结 http://formilyjs.org/#/0yTeT0/jAU8UVSYI8