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函数进行数据验证。大致的执行过程如下
- 遍历外部传入的规则配置项,根据配置项中的type数据类型,获取对应数据类型的validator验证函数,得到最终的验证规则集合。
- 遍历最终的规则集合,执行规则的validator验证函数, 处理、收集验证函数的验证结果。
- 所有规则执行完成后,触发外部传递的callback完成函数,并且传递收集到的验证结果。
最后
async-validator中非核心流程的部分经过了省略。
以上只是我对async-validator的一点理解,希望我们能一起学习、一起进步。
最后,你可以从功能的实现、代码的组织、可读性等任何的角度思考下async-validator中做得比较好或者能够优化的地方吗?更多关于async-validator原理的资料请关注编程网其它相关文章!