DripForm源码分析(编写ajv插件)

·  阅读 205

「这是我参与2022首次更文挑战的第2天,活动详情查看:2022首次更文挑战」。

介绍

从DripForm的源码中可以看到两个子包drip-form-plugin-formats和drip-form-plugin-keywords,DripForm利用ajv提供动态表单的校验能力,当然ajv可能默认提供的能力并不能很好的满足需求,和大多数工具一样,开放了插槽式的体验,能够对原本处理代码的流程进行扩展,加入自定的formats和keywords等。在开发ajv的插件时,建议遵循如下规则:

  1. 插件所导出的函数需要满足第一个参数是ajv的实例
  2. 函数需要满足返回值也是一个ajv实例,便于链式调用
  3. 导出的函数能够接受一个可选的配置作为第二个参数

ajv插件雏形

就拿DripForm开发的format插件,增加了两个format,提供了对color和date-time格式的支持。我们可以很容易的从上面的规则中写出如下代码,根据ajv官网对自定义插件的描述,我们在使用typescript的同时,可以导入ajv的Plugin类型支持。

import type { Plugin, Options } from 'ajv/dist/2019';
const addFormat: Plugin<Options> = (ajv, options) => {
  return ajv;
};
export default addFormat;
复制代码

ajv添加format的要求

当我们要校验ajv默认未提供的类型时,又该怎么办呢?比如说要添加对颜色属性的格式校验,支持rgba、rgb、十六进制的色值。这时候ajv实例为我们提供了一个addFormat的方法,用以自定义format类型,参照官方的要求 ajv.addFormat(name: string, format: Format): Ajv,要提供一个Format类型的参数,name接受一个字符串,代表自定义的format类型名称。可以看到要么传递string、RegExp或者是一个范围值为boolean类型的函数,看到这里,就可以去实现自己的自定义类型了。


type Format =
  | true // to ignore this format (and pass validation)
  | string // will be converted to RegExp
  | RegExp
  | (data: string) => boolean
  | Object // format definition (see below and in types)
复制代码

编写addFormat的参数对象

对于color类型,设想的是接受一个string类型的参数,返回各种颜色表达格式的正则测试结果。对于复杂的颜色正则表达式校验,我们可以先校验是否是rgb或rgba开头的,如果成功了,再去进行完整的正则校验,这样可以提升正则表达式的性能,减少不必要的消耗。 color.png 对于date-time类型,相对而言就简单了许多,直接传入正则表达式就可以了。

const dateTimeReg = /^\d\d\d\d-[0-1]\d-[0-3]\d[t\s](?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)$/i;
export default dateTimeReg;
复制代码

将format与雏形函数进行整合

我们可以看到,自定义的addFormat函数中,还有一个options对象,该参数接受一个formats数组,数组里面的format属性将会被添加到ajv实例中,但是遇到未定义的format属性会抛出异常,除非在初始化的时候,配置取消严格模式。得先判断传入的是否是一个数组,如果是数组的话,就会校验自定义的formats属性名是否在该配置的数组里面,如果不在则调用ajv.addFormat()像ajv的实例中添加format规则。options未定义的话,将默认添加已经定义好的format。

const addFormat: Plugin<Options> = (ajv, options) => {
  if (Array.isArray(options)) {
    Object.keys(formats)
      .filter((item) => options.includes(item as formatsName))
      .map((item) => {
        ajv.addFormat(item, formats[itemas formatsName])
      })
  } else if (!options) {
    Object.keys(formats).map((item) => {
      ajv.addFormat(item, formats[item as formatsName])
    })
  }
  return ajv
}
复制代码
分类:
前端
标签:
分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改