tiodot / tiodot.github.io

总结归纳--做的东西不是每一件都留到现在 但是能通过实践,收获了如何构建一套系统,一套工具的方法论
https://xchb.work
8 stars 0 forks source link

React表单验证之二:如何实现一个验证器 #17

Open tiodot opened 7 years ago

tiodot commented 7 years ago

前言

16 中提到要实现一个能直接使用jsx语法表示的React表单验证器。因而至少需要达到以下两个要求:

  1. 内置一些基本的校验规则
  2. 支持自定义校验规则

在使用antd的form表单组件时,可以发现其已经实现这样的一个校验器了——async-validator,其基本用法可以参考async-validator使用,然后之前也分析过其源码,觉得其代码组织很好,将不同的基本校验规则拆分成不同的模块,然后以组合方式使用,支持的校验规则也很全面。但有一点,校验逻辑那块代码甚是复杂。于是基于async-validator,改造了一下其源码。

改造点

新增

async-validator没有提供动态添加校验规则的api,虽然可以实现,却也有点繁琐,故新增一个extendapi用于动态添加校验规则。

优化

  1. 简化其中的校验逻辑,也就是validateapi里面的代码逻辑
  2. 在定义校验规则或者扩展时,将校验规则映射到实际校验函数

    移除

    移除一些无关紧要,或者说使用频率较低的功能点:

  3. 移除对嵌套对象的验证
  4. 移除对firstField的支持

实现

整体概述: image 原理很简单,实现当然也不会很复杂:

  1. 定义一个类Schema来保存校验规则
  2. 提供输入校验规则的api,这里定义了两个,一个define,一个extend
  3. 提供一个验证的api——validate
  4. 提供一个扩展校验规则函数的api

代码结构为:

class Schema {
    constructor(descriptor) {
        this.rules = null;
        this.define(descriptor);
    }
    define(rules) {}
    extend(rules) {}
    validate(source, options = {}, callback) {}
    static register(type, validator) {}
}

rules的格式为:

{
  name: {type: "string", required: true} // name 为字段名称,其值为改字段的验证规则
}

define和extend都是接受校验规则,其唯一区别就是define会清空this.rules。 至于validate则是根据给的值,然后匹配对应的校验规则进行验证,核心代码为:

validate(source, options = {}, callback) {
        const keys = options.keys || Object.keys(this.rules);
        const errors = [];
        for (let key of keys) {
            const rules = this.rules[key];
            let value = source[key];
            for (let rule of rules) {
                let tempError = rule.validator(rule, value, source, options);
                // 直接返回string或者new Error('xxx')形式
                if (typeof tempError === 'string' || tempError.message) {
                    tempError = [tempError];
                }
                if (Array.isArray(tempError) && tempError.length) {
                    if (rule.message) {
                        tempError = [].concat(rule.message);
                    }
                    errors.push(...tempError);
                    break;
                }
            }
        }
        callback(errors.length ? errors : null);
    }

最后是提供一个校验规则函数的register接口,所有的校验规则函数都存储在validators,简单的说这个就是一个Object,其形式为:

validators = {
  required: function(rule, value, source, options){/*对value进行空值判断*/},
  number: function(rule, value, source, options){...}
  ....
}

所以扩展也极其简单:

static register(type, validator) {
  if (typeof validator !== 'function') {
    throw new Error('Cannot register a validator by type, validator is not a function');
  }
  validators[type] = validator;
}

参考

  1. Antd的验证器async-validator
  2. 改造后的具体源码可以参考react-validator/verification