关于json:Form-Schema-定义详解

102次阅读

共计 5276 个字符,预计需要花费 14 分钟才能阅读完成。

作者:汪曦

背景

上期分享了基于 Formily 的表单设计器实现原理,JSON Schema 是表单设计器和表单渲染组件之间沟通的语言。为了更深刻了解表单设计器的外围,本期为大家具体解读表单 Schema 具体格局及疾速入门实际。

Formily 提供了 JSON Schema、JSX Schema、纯 JSX 三种开发模式。因为 JSON 能够序列化保留到数据库中,所以 JSON Schema 的形式非常适合后端动静渲染表单,前端齐全不须要保护 schema,只需利用 Formliy 提供的 SchemaForm 来渲染后端返回的 schema 即可。咱们仅需通过 Form Builder 或者 Page Designer 之类的工具来输入 JSON Schema,而后交给 SchemaForm 或者 PageEngine 之类的组件来渲染。

阐明:

Form Schema 遵循 json schema spec 的形容标准动静生成表单。

JSON Schema 标准

JSON Schema 是一个社区推动的 JSON 文件协定,用于标准 JSON 文件内容。它与平台无关,能够形容任意简单的数据结构,相比 XML,JSON 的形容格局更加紧凑,可读性更好。JSON Schema 在 JSON 的格局上,退出了一些列的标准化属性,用于形容结构化数据。Formily 遵循 JSON Schema 应用最宽泛的 draft-07 规范,并在其标准上裁减了本人的属性。

咱们能够借助 ajv 这类 JSON Schema 验证工具,来意识不同 Schema 标准的区别,详情可参考 draft-07 (and draft-06)。也能够执行 npm i ajv,在 nodejs 下查看 drat-07 的标准:

require("ajv/dist/refs/json-schema-draft-07.json")

Form Schema 构造

Formily 的 Form Schema 将规范的 JSON Schema 的 properties 属性进行了裁减,关键字段阐明如下:

属性名 形容 类型
title 字段题目 React.ReactNode
name 字段所属的父节点属性名 string
description 字段形容 React.ReactNode
default 字段默认值 any
type 字段类型 string, object, array, number
enum 枚举字段 string[], number[], Array<{label: React.ReactNode, value: any}>
required 字段是否必填 string, bool
maximum 校验最大值 (大于) number
minimum 校验最小值 (小于) number
maxLength 校验最大长度 number
minLength 校验最小长度 number
pattern 正则校验规定 string, RegExp
properties 对象属性 {[key : string]:Schema}
items 数组形容 Schema, Schema[]
editable 字段是否可编辑 boolean
visible 字段是否可见 (数据 + 款式) boolean
display 字段款式是否可见 boolean
x-props 字段扩大属性 {[name: string]: any }
x-index 字段程序 number
x-component 字段 UI 组件名称,大小写不敏感 string
x-component-props 字段 UI 组件属性 {}

通过比照 Form Schema 和规范的 json-schema-spec,不难发现 x-propsx-componentx-component-props 等 x- 结尾的属性是新增的,这些属性被 Formily 用来管制组件的渲染、数据校验、联动以及定义副作用等等。

Form Schema 生成表单

了解了 Form Schema 的定义,咱们就能够借助 Formily 的 SchemaForm 组件来动静生成表单。

  1. 举个例子,通过如下代码片段就能够渲染出一个带用户名、明码的根本登录界面了。

    import React from 'react'
    import ReactDOM from 'react-dom'
    import {SchemaForm} from '@formily/next'
    import {Input} from '@project/components'
    
    const Title = () => (<h2 style={{display: 'flex', justifyContent: 'center'}}>
        登录
      </h2>
    )
    
    const schema = {
      type: 'object',
      properties: {
        title: {
          type: 'string',
          'x-component': 'Title'
        },
        username: {
          type: 'string',
          title: '用户名',
          'x-component': 'Input'
        },
        password: {
          type: 'string',
          title: '明码',
          'x-component': 'Input'
        }
      }
     }
    
    const App = () => {
      return (
        <SchemaForm
          components={{
            Input,Title
          }}
          onSubmit={console.log}
          schema={schema}
        />
      )
    }
    ReactDOM.render(<App />, document.getElementById('root'))

    阐明:

    SchemaForm 有两个属性很要害。

    • schema 属性是外界传入的 json schema,这个 schema 通常作为字符串存在后端,前端通过接口去获取,JSON.parse 之后交给 SchemaForm 渲染。整个过程,前端齐全不关怀 schema 的起源以及如何结构。
    • components 属性定义了 schema 渲染时的可用组件上下文。form schema 中每个 x-component 属性都是一个 string,x-component 作为 key,要在 components map 中找到 value 并且 value 是一个非法的组件 (能够是 react、vue、angular 的组件,formily 不和具体的 UI 库绑定),这样就能够渲染出具体的组件。

    效果图如下:

    以 react 为例,大抵的渲染过程是:

    const fieldSchema = get(formSchema, 'properties.field_x')
    const compName = fieldSchema['x-component']
    const compProps = fieldSchema['x-component-props']
    
    React.createElement(SchemaForm.components[compName], compProps)
  2. 了解了根本登录页面的渲染,咱们来看一个更加简单的 schema 示例,效果图如下:

    const schema = {
      "version": "1.0",
      "type": "object",
      "properties": {
        "radio": {
          "type": "string",
          "enum": [
            "1",
            "2",
            "3",
            "4"
          ],
          "title": "Radio",
          "name": "radio",
          "x-component": "radio"
        },
        "select": {
          "type": "string",
          "enum": [
            "1",
            "2",
            "3",
            "4"
          ],
          "title": "Select",
          "name": "select",
          "x-component": "select"
        },
        "checkbox": {
          "type": "string",
          "enum": [
            "1",
            "2",
            "3",
            "4"
          ],
          "title": "Checkbox",
          "name": "checkbox",
          "x-component": "checkbox"
        },
        "textarea": {
          "type": "string",
          "title": "TextArea",
          "name": "textarea",
          "x-component": "textarea"
        },
        "number": {
          "type": "number",
          "title": "数字抉择",
          "name": "number",
          "minimum": 0,
          "maximum": 100,
          "x-component": "numberpicker"
        },
        "boolean": {
          "type": "boolean",
          "title": "开关抉择",
          "name": "boolean",
          "x-component": "switch"
        },
        "date": {
          "version": "1.0",
          "key": "date",
          "type": "string",
          "title": "日期抉择",
          "name": "date",
          "x-component": "datepicker"
        },
        "daterange": {
          "type": "date",
          "title": "日期范畴",
          "default": [
            "2018-12-19",
            "2021-12-19"
          ],
          "name": "daterange",
          "x-component": "daterangepicker"
        },
        "upload": {
          "type": "array",
          "title": "卡片上传文件",
          "name": "upload",
          "x-component-props": {"listType": "card"},
          "x-component": "upload"
        },
        "range": {
          "type": "number",
          "title": "范畴抉择",
          "name": "range",
          "x-component-props": {
            "min": 0,
            "max": 1024,
            "marks": [
              0,
              1024
            ]
          },
          "x-component": "range"
        },
        "transfer": {
          "type": "number",
          "enum": [
            {
              "key": 1,
              "title": "选项 1"
            },
            {
              "key": 2,
              "title": "选项 2"
            }
          ],
          "x-component-props": {},
          "title": "穿梭框",
          "name": "transfer",
          "x-component": "transfer"
        },
        "rating": {
          "type": "number",
          "title": "等级",
          "name": "rating",
          "x-component": "rating"
        }
      }
    }
    
    const components = {// 可用的组件上下文}
    
    ReactDOM.render(<SchemaForm schema={schema} components={components} />, document.body)

总结

综上,用 JSON Schema 来形容表单适宜于低代码或者数据中台的疾速开发。前端不须要保护 schema,schema 能够存在后端,随便散发动静渲染。然而毛病是 JSON Schema 的表达能力没有 JSX 强,在解决简单交互时,前端还是须要应用 JSX。

另外 JSON Schema 在交互过程中的重复解析也是性能的瓶颈,尤其是 schema 的内容很多且表单须要做简单联动和批量更新时,性能问题更加显著。起因是 Formily 外部会对状态做深拷贝,同时也做了深度遍历脏检测,这种形式可能晋升用户体验,但在大数据场景下,就会呈现性能问题, 此时须要思考屏蔽 Formily 的部分反复渲染,回到 react 的整树渲染。

当然任何计划都只是解决了局部的问题,一个计划能满足 80% 的场景,剩下的 20% 能够回退到其它的计划。Everything is tradeoff。

咱们留神到 JSON Schema 是递归形容的,解析的函数也是递归的。更好的计划是将解析,转换这类 CPU 密集型工作转移到独自的线程,或者交给性能更高的工具来做(如:WebAssembly)。在后续的文章中,咱们会剖析大数据量下 JSON Schema 渲染的性能,以及摸索用 Web Worker,Wasm 来解决 CPU 密集型工作相比于目前的纯 JS 计划能带来多大的晋升,敬请期待。

援用链接

[1] json schema spec:https://json-schema.org/

[2] draft-07 规范:https://json-schema.org/speci…

[3] draft-07 (and draft-06):https://ajv.js.org/guide/sche…)

对于全象云

全象云平台(https://portal.clouden.io)是青云科技自主研发的低代码平台,是基于云原生、用于辅助构建企业各类数字化利用的工具和集成平台。

平台目前提供云上无代码和低代码两种利用开发模式,屏蔽了技术的复杂度。反对可视化设计器,让开发人员和业务用户可能通过简略的拖拽、参数配置等形式疾速实现利用开发。同时集成了 IDaaS 身份认证能力、容器 DevOps 能力,反对企业存量业务与全象云业务交融。平台还蕴含丰盛的开发接口和弱小的插件机制,开发者可依据须要一直拓展平台的利用能力。

全象云的愿景是:在企业生产经营的各个象限、各个环节提供软件构件或反对服务。

本文由博客一文多发平台 OpenWrite 公布!

正文完
 0