• async-validator 源码学习笔记(六):validate 方法


    系列文章:

    1、async-validator 源码学习(一):文档翻译

    2、async-validator 源码学习笔记(二):目录结构

    3、async-validator 源码学习笔记(三):rule

    4、async-validator 源码学习笔记(四):validator

    5、async-validator 源码学习笔记(五):Schema

    一、validate 介绍

    validate 是 async-validator 的核心方法,不仅需要掌握它的使用,也需要了解它的原理。

    使用

    validator.validate( source, [options], callback )
    .then(()=>{})
    .catch( ({errors, fields}) => {})

    参数

    • source 是需要验证的对象
    • options 是描述验证的处理选项的对象
    • callback 校验完成的回调函数

    返回值是一个 promise 对象

    • then 是校验通过执行。
    • catch 校验失败执行。
    • errors 是 error 的数组,fields 是一个对象,包含监听对象和 error 的数组。

    validate 方法校验的流程为:

    async-validator 源码学习笔记(六):validate 方法

     

    源码是如何定义 validate 方法的呢?

    二、validate 源码解读

    复制代码
    /*
    参数:
     source_ 即 source :校验的对象。
     o 即 options :描述验证处理选项。
     oc 即 callback:验证完成的回调函数。
    */
    _proto.validate = function validate(source_, o, oc) {
    ...
     var source = source_;
     var options = o;
     var callback = oc;
     ...
     return asyncMap(series, options, function (data, doIt) {
      ....
     },function (results) {
      complete(results);
     }, source);
    };
    复制代码

     

    validate 方法前半部分主要是在构造一个完整的 series 对象,返回的是 asyncMap 方法。我们来看看 validate 方法内部的几个方法,分别作用是什么。

    参数处理

    复制代码
    _proto.validate = function validate(source_, o, oc) {
    ...
     var source = source_;
     var options = o;
     var callback = oc;
      // options 是可选参数
     // 如果 options 是函数时,说明第二个是回调函数,options 是空对象
     if (typeof options === 'function') {
      callback = options;
      options = {};
     }
     ...
    };
    复制代码

     

    检查校验规则

    检查校验规则是否为空,为空的时候立即执行回调。

    复制代码
    if (!this.rules || Object.keys(this.rules).length === 0) {
     if (callback) {
      callback(null, source);
     }
     return Promise.resolve(source);
    }
    复制代码

     

    complete 函数

    complete 函数主要是为了整合 errors 数组和 fields 对象,然后用 callback 回调函数把它们返回。

    复制代码
    _proto.validate = function validate(source_, o, oc) {
     ...
     function complete(results) {
      var errors = [];
      var fields = {};
      // 定义 add 方法,给 errors 添加元素 error
      function add(e) {
       if (Array.isArray(e)) {
        var _errors;
            // 给 errors 添加 error
        errors = (_errors = errors).concat.apply(_errors, e);
       } else {
        errors.push(e);
       }
      }
     // 迭代 resaults 把 resaults 中的每个 error 都加入 errors  
     for (var i = 0; i < results.length; i++) {
      add(results[i]);
      }
      //如果最后结果 errors 为空,就返回 null
      if (!errors.length) {
       callback(null, source);
      } else {
       //把 errors 中相同 field 的 error 合并,转换为对象的形式
       fields = convertFieldsError(errors);
       // errors fields 回调传出参数
       callback(errors, fields);
      }
     }
     return asyncMap(series, options, function (data, doIt) {
       ....
      },function (results) {
      complete(results);
     }, source);
    };
    复制代码

     

    options.message

    messsage 主要是定义检验失败后的错误提示信息,官方提供了一个默认模板,我们也可以进行定制化,此处的 options.message 就是来处理到底使用哪个的?根据情况到底是使用默认还是合并。

    复制代码
    _proto.validate = function validate(source_, o, oc) {
     ...
     //如果 options 中有 message 属性
     if (options.messages) {
      // 创建一个 message ,使用的是默认 
      var messages$1 = this.messages();
      if (messages$1 === messages) {
       messages$1 = newMessages();
      }
       // 将options 的 message 与默认的 message 合并
       deepMerge(messages$1, options.messages);
       options.messages = messages$1;
      } else {
       // options 没有 message 属性
       options.messages = this.messages();
      }
      return asyncMap(series, options, function (data, doIt) {
       ....
      },function (results) {
        complete(results);
      }, source);
    };
    复制代码

     

    series 对象

    生成的 serise 对象,目的是为了统一最终的数据格式。

    复制代码
    _proto.validate = function validate(source_, o, oc) {
     ...
     var series = {};
     // keys 是 rule 的所有键
     var keys = options.keys || Object.keys(this.rules);
     keys.forEach(function (z) {
      // arr 存放 rules[z] 的一个数组
      var arr = _this2.rules[z];
      // value 存放 source[z] 是一个值或对象 
      var value = source[z];
      arr.forEach(function (r) {
       var rule = r;
       // 当有transform属性而且是个函数时,要提前把值转换
       if (typeof rule.transform === 'function') {
        // 浅拷贝下,打破引用
        if (source === source_) {
         source = _extends({}, source);
        }
        value = source[z] = rule.transform(value);
       }
       // 浅拷贝打破引用
       if (typeof rule === 'function') {
        rule = {
         validator: rule
         };
        } else {
         rule = _extends({}, rule);
        } // Fill validator. Skip if nothing need to validate
        rule.validator = _this2.getValidationMethod(rule);
        // 异常处理
        if (!rule.validator) {
          return;
         }
        rule.field = z;
        rule.fullField = rule.fullField || z;
        rule.type = _this2.getType(rule);
        // 生成完整的 series 
        series[z] = series[z] || [];
        series[z].push({
        rule: rule,
        value: value,
        source: source,
        field: z
       });
      });
     });
      return asyncMap(series, options, function (data, doIt) {
       ....
      },function (results) {
          complete(results);
      }, source);
    };
    复制代码

     

    asyncMap

    asyncMap 作为一个返回函数,不得不说它又是什么内容呢?

    异步迭代用的 asyncMap 函数并没有多长,它主要实现两个功能,第一是决定是串行还是并行的执行单步校验,第二个功能是实现异步,把整个迭代校验过程封装到一个 promise 中,实现了整体上的异步。

    复制代码
    function asyncMap(objArr, option, func, callback, source) {
     // 如果option.first选项为真,说明第一个error产生时就要报错
     if (option.first) {
      // pending 是一个promise
      var _pending = new Promise(function (resolve, reject) {
       // 定义一个函数next,这个函数先调用callback,参数是errors
       // 再根据errors的长度决定resolve还是reject
       var next = function next(errors) {
        callback(errors);
        // reject的时候,返回一个AsyncValidationError的实例
        // 实例化时第一个参数是errors数组,第二个参数是对象类型的errors
        return errors.length ? reject(new AsyncValidationError(errors, convertFieldsError(errors))) : resolve(source);
       };
         // 把对象扁平化为数组flattenArr
       var flattenArr = flattenObjArr(objArr);
       // 串行
       asyncSerialArray(flattenArr, func, next);
      });
        // 捕获error
      _pending["catch"](function (e) {
        return e;
      });
      return _pending;
      }
        // 如果option.first选项为假,说明所有的error都产生时才报错
      // 当指定字段的第一个校验规则产生error时调用callback,不再继续处理相同字段的校验规则。
      var firstFields = option.firstFields === true ? Object.keys(objArr) : option.firstFields || [];
      var objArrKeys = Object.keys(objArr);
      var objArrLength = objArrKeys.length;
      var total = 0;
      var results = [];
      // 这里定义的函数next和上面的类似,只不过多了total的判断
      var pending = new Promise(function (resolve, reject) {
        var next = function next(errors) {
          results.push.apply(results, errors);
          // 只有全部的校验完才能执行最后的callback和reject
          total++;
    
          if (total === objArrLength) {
            // 这个callback和reject/resolve是这个库既能回调函数又能promise的核心
            callback(results);
            return results.length ? reject(new AsyncValidationError(results, convertFieldsError(results))) : resolve(source);
          }
        };
    
        if (!objArrKeys.length) {
          callback(results);
          resolve(source);
        }
    // 当firstFields中指定了该key时,说明该字段的第一个校验失败产生时就停止并调用callback
        // 所以是串行的asyncSerialArray
        // 没有指定该key,说明该字段的校验error需要都产生,就并行asyncParallelArray
        objArrKeys.forEach(function (key) {
          var arr = objArr[key];
    
          if (firstFields.indexOf(key) !== -1) {
            asyncSerialArray(arr, func, next);
          } else {
            asyncParallelArray(arr, func, next);
          }
        });
      });
      // 捕获error,添加错误处理
      pending["catch"](function (e) {
        return e;
      });
      // 返回promise实例
      return pending;
    }
    复制代码

     

  • 相关阅读:
    ISO认证怎么办理,申请流程
    PM项目管理职责
    小程序中Echarts实现隐藏x轴,y轴,刻度线,网格
    与归并排序相关的一些问题
    [附源码]计算机毕业设计springboot疫情背景下社区互助服务系统
    如何给element el-date-picker日期组件设置时间按照时间禁用
    编译安装 Python
    ArcGIS Pro SDK 003 如何调用Toolbox
    E - Fire! (双向bfs)
    Springboot+vue的时间管理系统(有报告)。Javaee项目,springboot vue前后端分离项目。
  • 原文地址:https://www.cnblogs.com/web-learn/p/16076935.html