文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

async-validator实现原理源码解析

2023-01-11 12:02

关注

async-validator 介绍

async-validator是异步的验证数据是否合法有效的工具, 内置了不同数据类型的常见验证规则。

在需要对数据进行验证的场景中,都可以考虑使用async-validator。 很多流行的组件库的表单验证都是基于async-validator的数据验证实现,如elementui、ant-design-vue、iview等

async-validator 基本使用

import AsyncValidator from 'async-validator';
// 1.声明规则 descriptor
const descriptor = {
  name: [
    {
      type: 'string',
      required: true,
      message: 'name 字段不能为空!'
    },
    // 通过调用callback, 传递验证是否通过的结果
    {
      validator: (rule, value, callback) => {
        if (value === 'muji1') {
          return callback('name 不能等于 muji1');
        }
        return callback();
      }
    },
    // 通过返回Error实例, 表示验证不通过
    {
      validator: (rule, value) => {
        if (value === 'muji2') {
          return new Error('name 不能等于 muji2');
        }
        return true;
      }
    },
    // 通过返回Promise实例, 传递验证是否通过的结果
    {
      validator: (rule, value) => {
        return new Promise((resole, reject) => {
          if (value === 'muji3') {
            return reject('name 不能等于 muji3');
          }
          return resole();
        })
      }
    }
  ],
  age: {
    type: 'number',
    // 自定义验证规则. age字段不能小于18, 小于则提示 ‘年纪太小'
    validator: (rule, value, callback) => {
        if (value < 18) {
          // 通过执行callback传递数据验证的结果
          callback('年纪太小');
        } else {
          callback();
        }
    },
  },
};
// 2.创建async-validator实例
const validator = new AsyncValidator(descriptor);
// 3.数据源
const data = { name: 'muji', age: 16 }
// 4.执行验证
validator.validate(data, function(errors, fields) {
    if (!errors) {
        // 验证成功
        console.log('验证通过');
    } else {
        // 验证失败
        console.log('验证不通过', error);
    }
})

async-validator 源码分析

从上面的基本使用中可以看到, 使用async-validator的过程主要分为:

1.创建async-validator实例

2.执行实例的validate方法时,传入数据源进行验证

async-validator 源码-构造函数

我们先分析第一步创建async-validator实例时,async-validator的构造函数做了什么事情。

constructor(descriptor: Rules) {
    this.define(descriptor);
}
define(rules: Rules) {
    // 规则配置不能为空
    if (!rules) {
      throw new Error('Cannot configure a schema with no rules');
    }
    // 规则配置必须是对象
    if (typeof rules !== 'object' || Array.isArray(rules)) {
      throw new Error('Rules must be an object');
    }
    this.rules = {};
    // 统一字段的规则配置方式为数组形式。因为给字段配置验证规则时, 可存在对象、数组的配置方式(如下). 
    
    Object.keys(rules).forEach(name => {
      const item: Rule = rules[name];
      this.rules[name] = Array.isArray(item) ? item : [item];
    });
}

构造函数中只是执行了define方法。

而define方法内做了以下几步:

1.验证传入的验证规则是否合法。

2.统一字段的规则配置方式为数组形式

async-validator 源码-validate方法

 
validate(source: Values, options: any = {}, callback: any = () => {}): Promise<Values> {
    
    if (typeof options === 'function') {
      callback = options;
      options = {};
    }
    // 此处省略了部分非核心逻辑代码
    // series保存最终的数据验证的规则集合。
    const series: Record<string, RuleValuePackage[]> = {};
    
    const keys = options.keys || Object.keys(this.rules);
    keys.forEach(field => {
      const rules = this.rules[field];
      let value = source[field];
      rules.forEach(rule => {
        // 此处省略了部分非核心逻辑代码
        // 为rule添加validator验证器函数(每个规则都必须存在一个validator函数去处理数据的验证逻辑)
        // getValidationMethod就是获取该rule的validator验证函数。
        // 如果rule中存在自定义的validator配置,则直接返回。
        // 如果不存在,则尝试根据rule中的type数据类型获取内置的validator验证函数
        rule.validator = this.getValidationMethod(rule);
        if (!rule.validator) {
          return;
        }
        // 为rule补充字段、类型的信息
        rule.field = field;
        rule.fullField = rule.fullField || field;
        // 处理完rule后, 将该rule添加到series中
        series[field] = series[field] || [];
        series[field].push({
          rule,
          value,
          source,
          field: field,
        });
      });
    });
    
    return asyncMap(
      series,
      options,
      (data, next) => {
        // 每个规则的遍历回调。处理每条规则,并且执行规则中的验证函数validator (下面分析函数内具体逻辑)
      },
      errors => {
        // 完成回调。所有规则处理完成后执行的回调
      },
      source,
    );
  }

getValidationMethod 获取规则的数据验证函数源码

getValidationMethod(rule: InternalRuleItem) {
    // 存在自定义验证函数直接返回
    if (typeof rule.validator === 'function') {
      return rule.validator;
    }
    // 省略部分非核心逻辑代码
    // 根据指定的类型,获取对应的数据验证函数
    return validators[this.getType(rule)] || undefined;
}
// 根据规则配置项的配置,返回不同的类型
getType(rule: InternalRuleItem) {
    // 不存在type类型, pattern为正则,则使用pattern类型
    if (rule.type === undefined && rule.pattern instanceof RegExp) {
      rule.type = 'pattern';
    }
    // 省略部分非核心逻辑代码
    // 规则配置项中存在type则返回, 不存在则返回string类型
    return rule.type || 'string';
}
// async-validator中内置的数据类型。
var validators = {
  string: string,
  method: method,
  number: number,
  "boolean": _boolean,
  regexp: regexp,
  integer: integer,
  "float": floatFn,
  array: array,
  object: object,
  "enum": enumerable,
  pattern: pattern,
  date: date,
  url: type,
  hex: type,
  email: type,
  required: required,
  any: any
};

第二步中的getValidationMethod方法,为每个rule验证规则获取具体验证数据的validator验证函数。

到第三步后,遍历验证规则series集合,执行规则中的validator验证函数时,把数据传入validator函数中进行验证。

第三步完整代码


 return asyncMap(
   series,
   options,
   (data, next) => {
       const rule = data.rule;
       // 此处省略部分非核心逻辑
       let res: ValidateResult;
        
       if (rule.validator) {
           
          res = rule.validator(rule, data.value, cb, data.source, options);
        }
        
        if (res === true) {
          // validator返回true时,表示没有错误,直接执行cb进行下一个规则的验证。
          cb();
        } else if (res instanceof Error) {
           // validator返回Error时, 传递错误信息给cb函数, cb函数记录错误信息, 然后cb函数执行下一个规则的验证
          cb(res.message);
        } else if (res && (res as Promise<void>).then) {
          
          (res as Promise<void>).then(
            () => cb(),
            e => cb(e),
          );
        }
        
        function cb(e: SyncErrorType | SyncErrorType[] = []) {
          let errorList = Array.isArray(e) ? e : [e];
          if (errorList.length && rule.message !== undefined) {
            errorList = [].concat(rule.message);
          }
           
          let filledErrors = errorList.map(complementError(rule, source));
          // asyncMap并不是同步循环series规则集合,而是遍历的过程中,需要等待执行next才会遍历下一个series中的规则
          // 将错误结果filledErrors传递到下一个规则的事件循环中,最后所有规则验证完成时,能够获取到所有的规则的验证结果
          next(filledErrors);
        }
   },
   // errors 即所有验证不通过的错误记录(即执行next时传递的所有filledErrors)
   errors => {
        // 所有规则处理完成后执行的回调
        let fields: ValidateFieldsError = {};
        if (!errors.length) {
          // 不存在错误, 直接执行validate时传递的完成回调
          callback(null, source);
        } else {
          // 存在错误
          // 将errors错误记录按字段分类, 如每个字段可配置多条规则, 因此每个字段可能存在多个错误记录
          // fields 数据格式如 { field1: [error1, error2], field2: [error1] }
          fields = convertFieldsError(errors);
          // 执行完成回调, 传递errors错误记录, fields错误记录分类
          (callback as (
            errors: ValidateError[],
            fields: ValidateFieldsError,
          ) => void)(errors, fields);
        }
   },
   source,
 );

以上代码主要分为以下几步:

1.遍历验证的规则集合

2.执行每条规则的validator验证函数,进行数据验证。

3.验证完成后, 执行cb函数处理、记录验证的结果,然后cb执行next处理下一条规则。

4.所有规则遍历处理完后,触发调用validate时传入的callback,并传入验证结果。

async-validator 源码-register方法

在validators中注册新的validator数据验证器。

static function register(type: string, validator) {
    if (typeof validator !== 'function') {
      throw new Error(
        'Cannot register a validator by type, validator is not a function',
      );
    }
    // 将该type的validator数据验证器函数添加到validators中
    // 后续执行数据验证时,会根据type在validators中取验证器对数据进行验证
    validators[type] = validator;
};

总结

async-validator可以分为两个部分。
1.validators验证器集合: 保存着不同type数据类型的验证函数。可以通过register对validators进行扩展。

2.validate方法: 为rule规则根据type数据类型在validators验证器集合中匹配对应的validator函数进行数据验证。大致的执行过程如下

最后

async-validator中非核心流程的部分经过了省略。

以上只是我对async-validator的一点理解,希望我们能一起学习、一起进步。

最后,你可以从功能的实现、代码的组织、可读性等任何的角度思考下async-validator中做得比较好或者能够优化的地方吗?更多关于async-validator原理的资料请关注编程网其它相关文章!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     801人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     348人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     311人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     432人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     220人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-前端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯