async-validator 源码学习笔记validate 方法

首页 / 新闻资讯 / 正文

一、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 添加元素 errorfunction add(e) {if (Array.isArray(e)) {var _errors;// 给 errors 添加 error     errors = (_errors = errors).concat.apply(_errors, e);    }else {     errors.push(e);    }   }// 迭代 resaults 把 resaults 中的每个 error 都加入 errorsfor (var i = 0; i < results.length; i++) {   add(results[i]);   }//如果最后结果 errors 为空,就返回 nullif (!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 是一个promisevar _pending =new Promise(function (resolve, reject) {// 定义一个函数next,这个函数先调用callback,参数是errors// 再根据errors的长度决定resolve还是rejectvar next =function next(errors) {     callback(errors);// reject的时候,返回一个AsyncValidationError的实例// 实例化时第一个参数是errors数组,第二个参数是对象类型的errorsreturn errors.length ? reject(new AsyncValidationError(errors, convertFieldsError(errors))) : resolve(source);    };// 把对象扁平化为数组flattenArrvar 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; }