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

x33g5p2x  于2022-03-31 转载在 其他  
字(5.4k)|赞(0)|评价(0)|浏览(262)

系列文章:

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 方法校验的流程为:

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

二、validate 源码解读

/*参数:
 source_ 即 source :校验的对象。
 o 即 options :描述验证处理选项。
 oc 即 callback:验证完成的回调函数。
*/_proto.validate = functionvalidate(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 = functionvalidate(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);
 }
 returnPromise.resolve(source);
}

complete 函数

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

_proto.validate = functionvalidate(source_, o, oc) {
 ...
 functioncomplete(results) {
  var errors =[];
  var fields ={};
  //定义 add 方法,给 errors 添加元素 error
  functionadd(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 = functionvalidate(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 = functionvalidate(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 中,实现了整体上的异步。

functionasyncMap(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 = functionnext(errors) {
    callback(errors);
    //reject的时候,返回一个AsyncValidationError的实例
    //实例化时第一个参数是errors数组,第二个参数是对象类型的errors
    return errors.length ? reject(newAsyncValidationError(errors, convertFieldsError(errors))) : resolve(source);
   };
     //把对象扁平化为数组flattenArr
   var flattenArr =flattenObjArr(objArr);
   //串行
asyncSerialArray(flattenArr, func, next);
  });
    //捕获error
  _pending["catch"](function(e) {
    returne;
  });
  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 = functionnext(errors) {
      results.push.apply(results, errors);
      //只有全部的校验完才能执行最后的callback和reject
      total++;

      if (total ===objArrLength) {
        //这个callback和reject/resolve是这个库既能回调函数又能promise的核心
callback(results);
        return results.length ? reject(newAsyncValidationError(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) {
    returne;
  });
  //返回promise实例
  returnpending;
}

相关文章