JSON SCHEMA
有官网,开源的草案。JSON SCHEMA官网
本项目最终产出的项目就是基于JSON SCHEMA实现,目的就是表单的自动化装配。
JSON SCHEMA:用json定义数据,并校验数据。
一些名称的定义最好参考官网的建议。
AJV
AJV 官网
一个JSON SCHEMA的校验器。
AJV最简单的用法(其实也没有别的用法)

核心功能就是校验一份数据是不是我们想要的schema,如果可以返回收到每一个不是的标记的地方。
安装AJV
npm install ajv
新建schema-tests\test1.js:
const Ajv = require('ajv')
const ajv = new Ajv() // options can be passed, e.g. {allErrors: true}
const schema = {
  type: 'string',
  minLength: 10
}
//生成schema校验规则
const validate = ajv.compile(schema)
const data = 'jokcy'
// 校验数据是否符合规则
const valid = validate(data)
if (!valid) console.log(validate.errors)
代码会有很多eslint报错,新增.eslintignore文件,忽略一下整个文件夹:
schema-tests
执行js文件

如果传入一个数字:
const Ajv = require('ajv')
const ajv = new Ajv() // options can be passed, e.g. {allErrors: true}
const schema = {
  type: 'string',
  minLength: 10
}
//生成schema校验规则
const validate = ajv.compile(schema)
const data = 12
// 校验数据是否符合规则
const valid = validate(data)
if (!valid) console.log(validate.errors)

稍微复杂一下:
const Ajv = require('ajv')
const ajv = new Ajv() // options can be passed, e.g. {allErrors: true}
const schema = {
  type: 'object',
  properties: {
    name: {
      type: "string",
      // minLength: 10,
    },
    age: {
      type: "number"
    },
    pets: {
      type: 'array',
      items: {
        type: 'string'
      }
    },
    isWorker: {
      type: 'boolean'
    }
  },
}
//生成schema校验规则
const validate = ajv.compile(schema)
const data = {
  name: 'jokcy',
  age: 18,
  pets: ['mimi', 'wangcai'],
  isWorker: true
}
// 校验数据是否符合规则
const valid = validate(data)
if (!valid) console.log(validate.errors)
运行正常,没有打印数据:

非空校验如下编写:
const Ajv = require('ajv')
const ajv = new Ajv() // options can be passed, e.g. {allErrors: true}
const schema = {
  type: 'object',
  properties: {
    name: {
      type: "string",
      // minLength: 10,
    },
    age: {
      type: "number"
    },
    pets: {
      type: 'array',
      items: {
        type: 'string'
      }
    },
    isWorker: {
      type: 'boolean'
    }
  },
  required: ['name', 'age']
}
//生成schema校验规则
const validate = ajv.compile(schema)
const data = {
  name: 'jokcy',
  pets: ['mimi', 'wangcai'],
  isWorker: true
}
// 校验数据是否符合规则
const valid = validate(data)
if (!valid) console.log(validate.errors)

还可以更深入地限制数组:
const Ajv = require('ajv')
const ajv = new Ajv() // options can be passed, e.g. {allErrors: true}
const schema = {
  type: 'object',
  properties: {
    name: {
      type: "string",
      // minLength: 10,
    },
    age: {
      type: "number"
    },
    pets: {
      type: 'array',
      items: [{
        type: 'string'
      }, {
        type: 'number'
      }] //数组只能有2个值,第一个值必须是string,第二个值必须是数字
    },
    isWorker: {
      type: 'boolean'
    }
  },
  required: ['name', 'age']
}
//生成schema校验规则
const validate = ajv.compile(schema)
const data = {
  name: 'jokcy',
  age: 18,
  pets: ['mimi', 'wangcai'],
  isWorker: true
}
// 校验数据是否符合规则
const valid = validate(data)
if (!valid) console.log(validate.errors)
Format
格式校验。format只针对String和number,其他类型没有此属性。

自定义format
const Ajv = require('ajv')
const ajv = new Ajv() // options can be passed, e.g. {allErrors: true}
const schema = {
  type: 'object',
  properties: {
    name: {
      type: "string",
      format: "test"
      // minLength: 10,
    },
    age: {
      type: "number"
    },
    pets: {
      type: 'array',
      // items: [{
      //   type: 'string'
      // }, {
      //   type: 'number'
      // }] //数组只能有2个值,第一个值必须是string,第二个值必须是数字
    },
    isWorker: {
      type: 'boolean'
    }
  },
  required: ['name', 'age']
}
ajv.addFormat('test', (data) => {
  console.log(data, '------------------------')
  return data === 'haha'
})
//生成schema校验规则
const validate = ajv.compile(schema)
const data = {
  name: 'jokcy',
  age: 18,
  pets: ['mimi', 80],
  isWorker: true
}
// 校验数据是否符合规则
const valid = validate(data)
if (!valid) console.log(validate.errors)

怎么通过,修改传入数据的data的name为haha即可:
const data = {
  name: 'haha',
  age: 18,
  pets: ['mimi', 80],
  isWorker: true
}

ajv中自定义关键字
自定义关键字有4种方法。

const Ajv = require('ajv')
const ajv = new Ajv() // options can be passed, e.g. {allErrors: true}
const schema = {
  type: 'object',
  properties: {
    name: {
      type: "string",
      test: true,
    },
    age: {
      type: "number"
    },
    pets: {
      type: 'array',
    },
    isWorker: {
      type: 'boolean'
    }
  },
  required: ['name', 'age']
}
ajv.addKeyword('test', {
  validate(schema, data) {
    console.log(schema, data)
    return true;
  }
})
//生成schema校验规则
const validate = ajv.compile(schema)
const data = {
  name: 'jokcy',
  age: 18,
  pets: ['mimi', 'wangcai'],
  isWorker: true
}
// 校验数据是否符合规则
const valid = validate(data)
if (!valid) console.log(validate.errors)

const Ajv = require('ajv')
const ajv = new Ajv() // options can be passed, e.g. {allErrors: true}
const schema = {
  type: 'object',
  properties: {
    name: {
      type: "string",
      test: true,
    },
    age: {
      type: "number"
    },
    pets: {
      type: 'array',
    },
    isWorker: {
      type: 'boolean'
    }
  },
  required: ['name', 'age']
}
ajv.addKeyword('test', {
  compile(sch, parentSchema) {
    console.log(sch, parentSchema)
    return () => true
  }
  // validate(schema, data) {
  //   console.log(schema, data)
  //   return true;
  // }
})
//生成schema校验规则
const validate = ajv.compile(schema)
const data = {
  name: 'jokcy',
  age: 18,
  pets: ['mimi', 'wangcai'],
  isWorker: true
}
// 校验数据是否符合规则
const valid = validate(data)
if (!valid) console.log(validate.errors)

compile可以根据相应的配置返回对应的函数。
const Ajv = require('ajv')
const ajv = new Ajv() // options can be passed, e.g. {allErrors: true}
const schema = {
  type: 'object',
  properties: {
    name: {
      type: "string",
      test: true,
    },
    age: {
      type: "number"
    },
    pets: {
      type: 'array',
    },
    isWorker: {
      type: 'boolean'
    }
  },
  required: ['name', 'age']
}
ajv.addKeyword('test', {
  compile(sch, parentSchema) {
    console.log(sch, parentSchema)
    return () => true
  },
  metaSchema: {
    type: 'boolean',
  }
  // validate(schema, data) {
  //   console.log(schema, data)
  //   return true;
  // }
})
//生成schema校验规则
const validate = ajv.compile(schema)
const data = {
  name: 'jokcy',
  age: 18,
  pets: ['mimi', 'wangcai'],
  isWorker: true
}
// 校验数据是否符合规则
const valid = validate(data)
if (!valid) console.log(validate.errors)
metaSchema值类型的限制检测。

const Ajv = require('ajv')
const ajv = new Ajv() // options can be passed, e.g. {allErrors: true}
const schema = {
  type: 'object',
  properties: {
    name: {
      type: "string",
      test: true,
    },
    age: {
      type: "number"
    },
    pets: {
      type: 'array',
    },
    isWorker: {
      type: 'boolean'
    }
  },
  required: ['name', 'age']
}
ajv.addKeyword('test', {
  // compile(sch, parentSchema) {
  //   console.log(sch, parentSchema)
  //   return () => true
  // },
  // metaSchema: {
  //   type: 'boolean',
  // },
  macro(schema, parentSchema) {
    return {
      minLength: 10
    }
  }
  // validate(schema, data) {
  //   console.log(schema, data)
  //   return true;
  // }
})
//生成schema校验规则
const validate = ajv.compile(schema)
const data = {
  name: 'jokcy',
  age: 18,
  pets: ['mimi', 'wangcai'],
  isWorker: true
}
// 校验数据是否符合规则
const valid = validate(data)
if (!valid) console.log(validate.errors)

macro:相当于一个关键字申明了多个系统的schema。
错误信息语言改成中文
首先安装:
 npm install ajv-i18n  -S

const Ajv = require('ajv')
const ajv = new Ajv() // options can be passed, e.g. {allErrors: true}
var localize = require('ajv-i18n')
const schema = {
  type: 'object',
  properties: {
    name: {
      type: "string",
      test: true,
    },
    age: {
      type: "number"
    },
    pets: {
      type: 'array',
    },
    isWorker: {
      type: 'boolean'
    }
  },
  required: ['name', 'age']
}
ajv.addKeyword('test', {
  // compile(sch, parentSchema) {
  //   console.log(sch, parentSchema)
  //   return () => true
  // },
  // metaSchema: {
  //   type: 'boolean',
  // },
  macro(schema, parentSchema) {
    return {
      minLength: 10
    }
  }
  // validate(schema, data) {
  //   console.log(schema, data)
  //   return true;
  // }
})
//生成schema校验规则
const validate = ajv.compile(schema)
const data = {
  name: 'jokcy',
  age: 18,
  pets: ['mimi', 'wangcai'],
  isWorker: true
}
// 校验数据是否符合规则
const valid = validate(data)
if (!valid) {
  localize.zh(validate.errors);
  console.log(validate.errors)
}

自定义报错信息:
  validate: function fun(schema, data) {
    // console.log(schema, data)
    fun.errors = [{
      instancePath: '/name',
      schemaPath: '#/properties/name/test',
      keyword: 'test',
      params: {},
      message: '总是校验不通过的'
    }]
    return false;
  }
自定义校验结果不通过时的报错信息自定义
安装:
npm install ajv-errors -S
使用:先包装一下使用对象:
const ajv = new Ajv({
  allErrors: true
}) 
require('ajv-errors')(ajv)
完整demo:
const Ajv = require('ajv')
const ajv = new Ajv({
  allErrors: true,
  jsonPointers: true
}) // options can be passed, e.g. {allErrors: true}
var localize = require('ajv-i18n')
const schema = {
  type: 'object',
  properties: {
    name: {
      type: "string",
      // test: true,
      errorMessage: "这是不对的",
      minLength: 10
    },
    age: {
      type: "number"
    },
    pets: {
      type: 'array',
    },
    isWorker: {
      type: 'boolean'
    }
  },
  required: ['name', 'age']
}
require('ajv-errors')(ajv)
ajv.addKeyword('test', {
  // compile(sch, parentSchema) {
  //   console.log(sch, parentSchema)
  //   return () => true
  // },
  // metaSchema: {
  //   type: 'boolean',
  // },
  macro(schema, parentSchema) {
    return {
      minLength: 10
    }
  },
  validate: function fun(schema, data) {
    // console.log(schema, data)
    fun.errors = [{
      instancePath: '/name',
      schemaPath: '#/properties/name/test',
      keyword: 'test',
      params: {},
      message: '总是校验不通过的'
    }]
    return false;
  }
})
//生成schema校验规则
const validate = ajv.compile(schema)
const data = {
  name: 'jokcy',
  age: 18,
  pets: ['mimi', 'wangcai'],
  isWorker: true
}
// 校验数据是否符合规则
const valid = validate(data)
if (!valid) {
  localize.zh(validate.errors);
  console.log(validate.errors)
}
不同的对象传递不同的报错信息:
const schema = {
  type: "object",
  required: ["foo", "bar"],
  properties: {
    foo: {type: "integer"},
    bar: {type: "string"},
  },
  errorMessage: {
    type: "should be an object", // will not replace internal "type" error for the property "foo"
    required: {
      foo: 'should have an integer property "foo"',
      bar: 'should have a string property "bar"',
    },
  },
}
类库实现
考虑将来的扩展性、可以自定义的能力、非标准的功能如何方便的去编写。
接口,即props。
props涉及包括但不限于:schema、value、locale(语言包)、onChange、uiSchema(界面交互schema)
定义如下:
### API 设计
```jsx
<JsonSchemaForm
  schema={schema}
  value={value}
  onChange={handleChange}
  locale={locale}
  contextRef={someRef}
  uiSchema={uiSchema}
/>
schema
json schema 对象,用来定义数据,同事定义表单的依据
value
表单的数据结果,可以从外部改变 value,在表单被编辑的时候,会通过 onChange 透出 value
注意:因为 vue 使用可变数据,如果每次变化都去改变 value 的对象地址,会导致重新渲染,性能降低。
从实践中看,穿肚对象在内部修改其 field 的值不会有什么副作用。也就是说,如果value 是一个对象,从JsonSchemaForm 内部修改的值并不会修改 value 本身,但仍需要去触发onChange,因为可能在表单变化之后,使用者需要进行一些操作
onChage
在表单值有任何变化时触发回调方法,并把新的值返回
locale
语言,使用ajv-i18n指定错误信息使用的语言
contextRef
需要传入一个 vue3 的Ref对象,会在对象挂载doValidate方法,可以通过如下实现表单主动校验:
const yourRef=ref({})
onMounted(()=>{
  yourRef.value.doValidate()
})
<JsonSchemaForm contextRef={yourRef}/>
vue2 的实现:
<Comp ref="comp"/>
this.$ref.comp.xxx()
uiSchema
对表单的展现进行一些定制,类型如下:
export interface VueJsonSchemaConfig{
  title?:string
  descrription?:string
  component?:string
  additionProps?:{
    [key:string]:any
  }
  withFormItem?:boolean
  widget?:'checkbox'|'textarea'|'select'|'radio'|'range'|string
  items?:UISchema|UISchema[]
}
export interface UISchema extends VueJsonSchemaConfig{
  properties?:
    [property:string]:UISchema
}








