背景
在应用ivew.design的时候,在源码中发现form表单的验证是应用Async Validator,而后就去看一下源码,理解原理并做一下整顿。
const validator = new AsyncValidator(descriptor);let model = {};model[this.prop] = this.fieldValue;validator.validate(model, { firstFields: true }, errors => { this.validateState = !errors ? 'success' : 'error'; this.validateMessage = errors ? errors[0].message : ''; callback(this.validateMessage); this.FormInstance && this.FormInstance.$emit('on-validate', this.prop, !errors, this.validateMessage || null);});
这个是源码的版本
先理解咋用
- gitHub源码地址
star还是蛮多的,很多库都用了这个,只能说666~
装置
npm i async-validator
- 用法
更多的细节就从源码外面去阐明了,说多了也记不得~
总体解析
- 大抵设计如下
- 最初导出的Schema构造函数
由下面能够看出,这个js库最初导出Schema构造函数,并且分成几个大块,原型链下面的办法,register,warning,messages,validators
warning办法
在实例化 Schema 之前设置 warning 办法。该办法实际上是util.js文件外面的一个工具办法
如果放弃所有的管制太正告,你能够本人初始化Schema.warning = () => {}
//默认是一个空函数,这样if条件没有通过时将不会在控制台打印正告export let warning = () => {};// 在生产环境或者node runtime时,不打印正告信息if ( typeof process !== 'undefined' && process.env && process.env.NODE_ENV !== 'production' && typeof window !== 'undefined' && typeof document !== 'undefined') { warning = (type, errors) => { if (typeof console !== 'undefined' && console.warn) { if (errors.every(e => typeof e === 'string')) { //实际上就是一个console.warn console.warn(type, errors); } } };}
messages办法
在 Schema
中的message办法实际上是message.js文件中导出的实例,是依据不同类型的失败校验的提醒用的音讯模板.
//index.js//构造函数 function Schema(descriptor) { //descriptor申明了校验规定 this.rules = null; //应用公有属性_messages保留defaultMessages this._messages = defaultMessages; this.define(descriptor);}Schema.messages = defaultMessages;//message.jsexport function newMessages() { return { default: 'Validation error on field %s', required: '%s is required', enum: '%s must be one of %s', whitespace: '%s cannot be empty', date: { format: '%s date %s is invalid for format %s', parse: '%s date could not be parsed, %s is invalid ', invalid: '%s date %s is invalid', }, types: { string: '%s is not a %s', method: '%s is not a %s (function)', array: '%s is not an %s', object: '%s is not an %s', number: '%s is not a %s', date: '%s is not a %s', boolean: '%s is not a %s', integer: '%s is not an %s', float: '%s is not a %s', regexp: '%s is not a valid %s', email: '%s is not a valid %s', url: '%s is not a valid %s', hex: '%s is not a valid %s', }, string: { len: '%s must be exactly %s characters', min: '%s must be at least %s characters', max: '%s cannot be longer than %s characters', range: '%s must be between %s and %s characters', }, number: { len: '%s must equal %s', min: '%s cannot be less than %s', max: '%s cannot be greater than %s', range: '%s must be between %s and %s', }, array: { len: '%s must be exactly %s in length', min: '%s cannot be less than %s in length', max: '%s cannot be greater than %s in length', range: '%s must be between %s and %s in length', }, pattern: { mismatch: '%s value %s does not match pattern %s', }, clone() { // 深拷贝 const cloned = JSON.parse(JSON.stringify(this)); cloned.clone = this.clone; return cloned; }, };}export const messages = newMessages();
当然有必要的话,这个messages的模板本人也是能够革新的。
//index.js//Schema.prototype的原型办法中有message的办法messages(messages) { if (messages) { //将 _messages和参数 深度合并合并 this._messages = deepMerge(newMessages(), messages); } return this._messages;}//util.js//deepMerge是util.js中的一个一般合并对象的办法,先是遍历一遍对象,而后对下一层的对象应用构造,实际上只有2层的合并export function deepMerge(target, source) { if (source) { for (const s in source) { if (source.hasOwnProperty(s)) { const value = source[s]; if (typeof value === 'object' && typeof target[s] === 'object') { target[s] = { ...target[s], ...value, }; } else { target[s] = value; } } } } return target;}
//gitHub给到的例子import Schema from 'async-validator';const cn = { required: '%s 必填',};const descriptor = { name: { type: 'string', required: true } };const validator = new Schema(descriptor);// 将cn与defaultMessages深层合并validator.messages(cn);...
validators
- 为用户提供的各种数据类型的验证办法
import validators from './validator/index';Schema.validators = validators;
以对string类型的判断为例
- rule: 在源descriptor中,与要校验的字段名称绝对应的校验规定。始终为它调配一个field属性,其中蕴含要验证的字段的名称。
//这个样子{ [field: string]: RuleItem | RuleItem[]}//例子{name:{type: "string", required: true, message: "Name is required"}}
- value: 源对象属性中要校验的值。
- callback: 校验实现后须要调用的callback。传递一个Error实例数组以判断校验失败。如果校验是同步的,则能够间接返回false、Error或Error Array
callback(errors)
- source: 传给validate 办法的源对象
- options: 额定选项
//options的外部属性export interface ValidateOption { // 是否勾销对于有效值的外部正告 suppressWarning?: boolean; // 当第一个验证规定生成谬误时,进行解决 first?: boolean; //当指定字段的第一个验证规定生成谬误时,进行解决字段,“true”示意所有字段。 firstFields?: boolean | string[];}
- options.messages: 蕴含校验 error message 的对象,将与 defaultMessages 进行深度合并
//所有的验证的办法都是以rule, value, callback, source, options为参数function string(rule, value, callback, source, options) { // 须要callback进来的谬误列表 const errors = []; //首先验证required为false或者还未填写状态就间接返回 const validate = rule.required || (!rule.required && source.hasOwnProperty(rule.field)); if (validate) { //isEmptyValue判断是否为空值,并且将空数组也判断为true if (isEmptyValue(value, 'string') && !rule.required) { return callback(); } //应用rules办法判断是否必填 rules.required(rule, value, source, errors, options, 'string'); if (!isEmptyValue(value, 'string')) { // 判断type,range范畴(这里有波及了len,min,max判断),以及提供正则表达式的判断 rules.type(rule, value, source, errors, options); rules.range(rule, value, source, errors, options); rules.pattern(rule, value, source, errors, options); if (rule.whitespace === true) { //为仅由空格组成的字符串增加额定的校验 rules.whitespace(rule, value, source, errors, options); } } } callback(errors);}export default string;
register
除了上述提供的validators的办法,这个register用于自定义判断
Schema.register = function register(type, validator) { //必须是函数 if (typeof validator !== 'function') { throw new Error( 'Cannot register a validator by type, validator is not a function', ); } validators[type] = validator;};
为validators验证的办法提供的rule
/** * 所有的办法的传值如下 * @param rule 校验的规定 * @param value source对象中该字段的值 * @param source 要校验的source对象 * @param errors 本次校验将要去增加的errors数组 * @param options 校验选项 * @param options.messages 校验的messages */export default { required, //属性为必填 whitespace, //校验空白字符 type, //判断type属性 range, //通过传入的number类型的len,min,max 进行判断 enum: enumRule,//校验值是否存在在枚举值列表中 pattern,//校验是否合乎校验正则表达式};
type有如下类型
const custom = [ 'integer', 'float', 'array', 'regexp', 'object', 'method', 'email', 'number', 'date', 'url', 'hex', ];
// 枚举验证 官网案例const descriptor = { role: { type: 'enum', enum: ['admin', 'user', 'guest'] },};
原型链下面的办法
- messages
//上文有交代,用于新增本人的谬误音讯提醒模板 messages(messages) { if (messages) { this._messages = deepMerge(newMessages(), messages); } return this._messages;},
- define
注册校验规定rules
- getType
通过此办法取得rule的type,并会判断此type是否是曾经定义好的validators中的type
- getValidationMethod
通过此办法取得rule的validator
getValidationMethod(rule) { //定义好了validator间接返回 if (typeof rule.validator === 'function') { return rule.validator; } //收集单个rule外面定义的所有key const keys = Object.keys(rule); const messageIndex = keys.indexOf('message'); if (messageIndex !== -1) { //删除message属性 keys.splice(messageIndex, 1); } //除了message,就只有一个key且是required,返回validators提供的required办法 if (keys.length === 1 && keys[0] === 'required') { return validators.required; } //否则,最初一种状况,依据rule定义的type判断 // 如果type是非法未定义的type则间接返回false return validators[this.getType(rule)] || false; },
- validate
外围办法 1. 参数: source_ 须要校验的对象 o 即options 形容校验的解决选项的对象(上文说的,suppressWarning,firstFields,first) oc 即callback 校验实现后的回调函数 2. 返回值是一个Promise对象: then 校验通过 catch({errors,fields}) 校验失败 errors是一个所有error数组,fiels是一个相似{field1:[error,error...],field2:[error,error...]}的对象
不得不说asyncMap又是怎么内容
首先它。若options.first为否值,;再依据options.firstFields是否为真值,别离执行asyncSerialArray、asyncParallelArray函数。
export function asyncMap(objArr, option, func, callback) { //判断first if (option.first) { //判断options.first是否为真值,若为真值,调用asyncSerialArray解决series数组 //当某一规定校验失败时,即终止校验,执行callback回调 const pending = new Promise((resolve, reject) => { const next = errors => { callback(errors); return errors.length ? reject(new AsyncValidationError(errors, convertFieldsError(errors))) : resolve(); }; const flattenArr = flattenObjArr(objArr); asyncSerialArray(flattenArr, func, next); }); pending.catch(e => e); return pending; } let firstFields = option.firstFields || []; if (firstFields === true) { firstFields = Object.keys(objArr); } const objArrKeys = Object.keys(objArr); const objArrLength = objArrKeys.length; let total = 0; const results = []; const pending = new Promise((resolve, reject) => { //构建next函数包装callback,目标是将所有校验器的失败文案合并再传入callback const next = errors => { results.push.apply(results, errors); total++; if (total === objArrLength) { callback(results); return results.length ? reject( new AsyncValidationError(results, convertFieldsError(results)), ) : resolve(); } }; if (!objArrKeys.length) { callback(results); resolve(); } objArrKeys.forEach(key => { const arr = objArr[key]; //判断firstFields值,别离执行asyncSerialArray、asyncParallelArray函数 //asyncParallelArray函数用于实现平行校验,在某个异步校验器执行过程中,平行调用下一个校验器; //asyncSerialArray用于实现有序校验,在异步校验器执行实现后,再启用下一个校验器。 if (firstFields.indexOf(key) !== -1) { asyncSerialArray(arr, func, next); } else { asyncParallelArray(arr, func, next); } }); }); pending.catch(e => e); return pending;}
最初
就这样了,有新的理解,前面再编辑,批改
喜爱,帮忙点赞????