上篇 async-validator 源码解析(二):rule 将 async-validator
校验库的 rule
目录下的代码进行了分析,下面继续来填坑分析 validator
目录下的源码,自底向上理解表单校验的原理。可以从仓库 https://github.com/MageeLin/async-validator-source-code-analysis 的analysis
分支看到本篇中的每个文件的代码分析。
已完成:
- async-validator 源码解析(一):文档翻译
- async-validator 源码解析(二):rule
- async-validator 源码解析(三):validator
- async-validator 源码解析(四):Schema 类
- async-validator 源码解析(五):校验方法 validate
依赖关系
代码依赖关系如下所示:
按照从底向上的方式,本篇主要分析 validator
目录。
validator
validator
和之前的 rule
关系非常密切,rule
目录下方法的主要功能是通过校验 value
和 rule
,来给 errors
数组添加新的 error
。而 validator
则是将 value
分成各种类型,然后对不同类型的 value
执行不同的 rule
校验组合,便于回调函数 callback
对最终的 errors
数组做进一步的处理。
- 该目录下的校验方法的结构基本类似,但是汇总的说,第一步是判断是否需要进行校验:
- 该字段是必需的。
- 该字段不是必需的,但是
source
对象中该字段有值且不为空值。
- 如果需要校验,第二步校验时是如下的步骤:
- 先校验该字段不为空的
rule
。
- 再校验该类型值对应的其他
rule
。
- 最后就执行
callback(errors)
,用回调函数调用 errors
,使用回调函数来进行之后的步骤。
如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
|
function foo(rule, value, callback, source, options) { const errors = []; const validate = rule.required || (!rule.required && source.hasOwnProperty(rule.field)); if (validate) { if (isEmptyValue(value, 'foo') && !rule.required) { return callback(); } rules.required(rule, value, source, errors, options); if (value !== undefined) { rules.foo(rule, value, source, errors, options); } } callback(errors); }
|
其实回想一下,在 Element-UI
中,我们为字段创建自定义的 validator
函数,参数也是 validator(rule, value, callback) {...}
(专门测试了下第 4
和第 5
个参数的确也是 source
和 options
),与 validator 目录中的各类型校验方法是一样的入参。而且校验成功时同样是什么都不返回
或者 callback()
,校验失败时同样是返回 callback(errors)
。所以在手写 validator
时就是在手写这个这个模板,rule
中的那些自定义参数也就是 validator
的类似语法糖简写。
string.js
下面以 string.js
为例,说明上述模板是如何用的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| import rules from '../rule/index.js'; import { isEmptyValue } from '../util';
function string(rule, value, callback, source, options) { const errors = []; const validate = rule.required || (!rule.required && source.hasOwnProperty(rule.field)); if (validate) { if (isEmptyValue(value, 'string') && !rule.required) { return callback(); } rules.required(rule, value, source, errors, options, 'string'); if (!isEmptyValue(value, 'string')) { 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;
|
index.js
index.js
是 validator
目录的统一出口管理,有一个比较有意思的地方是 url
、hex
和 email
这三种类型的校验其实本质上都是 type
校验。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| import string from './string'; import method from './method'; import number from './number'; import boolean from './boolean'; import regexp from './regexp'; import integer from './integer'; import float from './float'; import array from './array'; import object from './object'; import enumValidator from './enum'; import pattern from './pattern'; import date from './date'; import required from './required'; import type from './type'; import any from './any';
export default { string, method, number, boolean, regexp, integer, float, array, object, enum: enumValidator, pattern, date, url: type, hex: type, email: type, required, any, };
|
any.js
校验任意类型只需要一步,校验不为空即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| import rules from '../rule/index.js'; import { isEmptyValue } from '../util';
function any(rule, value, callback, source, options) { const errors = []; const validate = rule.required || (!rule.required && source.hasOwnProperty(rule.field)); if (validate) { if (isEmptyValue(value) && !rule.required) { return callback(); } rules.required(rule, value, source, errors, options); } callback(errors); }
export default any;
|
array.js
校验数组需要三步。第一步校验非空数组,第二步校验类型,第三步校验范围。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| import rules from '../rule/index'; import { isEmptyValue } from '../util';
function array(rule, value, callback, source, options) { const errors = []; const validate = rule.required || (!rule.required && source.hasOwnProperty(rule.field)); if (validate) { if (isEmptyValue(value, 'array') && !rule.required) { return callback(); } rules.required(rule, value, source, errors, options, 'array'); if (!isEmptyValue(value, 'array')) { rules.type(rule, value, source, errors, options); rules.range(rule, value, source, errors, options); } } callback(errors); }
export default array;
|
boolean.js
校验布尔值需要两步。第一步校验不为空值,第二步校验类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| import { isEmptyValue } from '../util'; import rules from '../rule/index.js';
function boolean(rule, value, callback, source, options) { const errors = []; const validate = rule.required || (!rule.required && source.hasOwnProperty(rule.field)); if (validate) { if (isEmptyValue(value) && !rule.required) { return callback(); } rules.required(rule, value, source, errors, options); if (value !== undefined) { rules.type(rule, value, source, errors, options); } } callback(errors); }
export default boolean;
|
date.js
校验时间需要三步。第一步校验不为空,第二步校验类型,第三步校验范围。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| import rules from '../rule/index.js'; import { isEmptyValue } from '../util';
function date(rule, value, callback, source, options) { const errors = []; const validate = rule.required || (!rule.required && source.hasOwnProperty(rule.field)); if (validate) { if (isEmptyValue(value, 'date') && !rule.required) { return callback(); } rules.required(rule, value, source, errors, options); if (!isEmptyValue(value, 'date')) { let dateObject;
if (value instanceof Date) { dateObject = value; } else { dateObject = new Date(value); }
rules.type(rule, dateObject, source, errors, options); if (dateObject) { rules.range(rule, dateObject.getTime(), source, errors, options); } } } callback(errors); }
export default date;
|
enum.js
校验枚举值需要两步,第一步校验不为空,第二步校验枚举。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| import rules from '../rule/index.js'; import { isEmptyValue } from '../util';
const ENUM = 'enum';
function enumerable(rule, value, callback, source, options) { const errors = []; const validate = rule.required || (!rule.required && source.hasOwnProperty(rule.field)); if (validate) { if (isEmptyValue(value) && !rule.required) { return callback(); } rules.required(rule, value, source, errors, options); if (value !== undefined) { rules[ENUM](rule, value, source, errors, options); } } callback(errors); }
export default enumerable;
|
float.js
校验浮点数需要三步。第一步校验不为空,第二步校验类型,第三步校验范围。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| import rules from '../rule/index.js'; import { isEmptyValue } from '../util';
function floatFn(rule, value, callback, source, options) { const errors = []; const validate = rule.required || (!rule.required && source.hasOwnProperty(rule.field)); if (validate) { if (isEmptyValue(value) && !rule.required) { return callback(); } rules.required(rule, value, source, errors, options); if (value !== undefined) { rules.type(rule, value, source, errors, options); rules.range(rule, value, source, errors, options); } } callback(errors); }
export default floatFn;
|
integer.js
校验整数需要三步。第一步校验不为空,第二步校验类型,第三步校验范围。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| import rules from '../rule/index.js'; import { isEmptyValue } from '../util';
function integer(rule, value, callback, source, options) { const errors = []; const validate = rule.required || (!rule.required && source.hasOwnProperty(rule.field)); if (validate) { if (isEmptyValue(value) && !rule.required) { return callback(); } rules.required(rule, value, source, errors, options); if (value !== undefined) { rules.type(rule, value, source, errors, options); rules.range(rule, value, source, errors, options); } } callback(errors); }
export default integer;
|
method.js
校验浮点数需要两步。第一步校验不为空,第二步校验类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| import rules from '../rule/index.js'; import { isEmptyValue } from '../util';
function method(rule, value, callback, source, options) { const errors = []; const validate = rule.required || (!rule.required && source.hasOwnProperty(rule.field)); if (validate) { if (isEmptyValue(value) && !rule.required) { return callback(); } rules.required(rule, value, source, errors, options); if (value !== undefined) { rules.type(rule, value, source, errors, options); } } callback(errors); }
export default method;
|
object.js
校验对象需要两步。第一步校验不为空,第二步校验类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| import rules from '../rule/index.js'; import { isEmptyValue } from '../util';
function object(rule, value, callback, source, options) { const errors = []; const validate = rule.required || (!rule.required && source.hasOwnProperty(rule.field)); if (validate) { if (isEmptyValue(value) && !rule.required) { return callback(); } rules.required(rule, value, source, errors, options); if (value !== undefined) { rules.type(rule, value, source, errors, options); } } callback(errors); }
export default object;
|
pattern.js
校验 pattern 需要两步。第一步校验不为空,第二步校验 pattern。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| import rules from '../rule/index.js'; import { isEmptyValue } from '../util';
function pattern(rule, value, callback, source, options) { const errors = []; const validate = rule.required || (!rule.required && source.hasOwnProperty(rule.field)); if (validate) { if (isEmptyValue(value, 'string') && !rule.required) { return callback(); } rules.required(rule, value, source, errors, options); if (!isEmptyValue(value, 'string')) { rules.pattern(rule, value, source, errors, options); } } callback(errors); }
export default pattern;
|
regexp.js
校验正则表达式需要两步。第一步校验不为空,第二步校验类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| import rules from '../rule/index.js'; import { isEmptyValue } from '../util';
function regexp(rule, value, callback, source, options) { const errors = []; const validate = rule.required || (!rule.required && source.hasOwnProperty(rule.field)); if (validate) { if (isEmptyValue(value) && !rule.required) { return callback(); } rules.required(rule, value, source, errors, options); if (!isEmptyValue(value)) { rules.type(rule, value, source, errors, options); } } callback(errors); }
export default regexp;
|
required.js
校验整数只需要一步,校验不为空。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| import rules from '../rule/index.js';
function required(rule, value, callback, source, options) { const errors = []; const type = Array.isArray(value) ? 'array' : typeof value; rules.required(rule, value, source, errors, options, type); callback(errors); }
export default required;
|
number.js
校验数字需要三步。第一步校验不为空,第二步校验类型,第三步校验范围。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| import rules from '../rule/index.js'; import { isEmptyValue } from '../util';
function number(rule, value, callback, source, options) { const errors = []; const validate = rule.required || (!rule.required && source.hasOwnProperty(rule.field)); if (validate) { if (value === '') { value = undefined; } if (isEmptyValue(value) && !rule.required) { return callback(); } rules.required(rule, value, source, errors, options); if (value !== undefined) { rules.type(rule, value, source, errors, options); rules.range(rule, value, source, errors, options); } } callback(errors); }
export default number;
|
type.js
校验整数需要两步。第一步校验不为空,第二步校验类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| import rules from '../rule/index.js'; import { isEmptyValue } from '../util';
function type(rule, value, callback, source, options) { const ruleType = rule.type; const errors = []; const validate = rule.required || (!rule.required && source.hasOwnProperty(rule.field)); if (validate) { if (isEmptyValue(value, ruleType) && !rule.required) { return callback(); } rules.required(rule, value, source, errors, options, ruleType); if (!isEmptyValue(value, ruleType)) { rules.type(rule, value, source, errors, options); } } callback(errors); }
export default type;
|
总结一下:
- 校验一步 ——
不为空
:required
、any
。
- 校验两步 ——
不为空 -> 类型
:type
、regexp
、pattern
、object
、method
、boolean
。enum
的第二步是校验枚举。
- 校验三步 ——
不为空 -> 类型 -> 范围
:number
、integer
、float
、date
、array
。