Bootstrap

async-validator实现原理源码解析

async-validator是异步的验证数据是否合法有效的工具, 内置了不同数据类型的常见验证规则。

在需要对数据进行验证的场景中,都可以考虑使用async-validator。 很多流行的组件库的表单验证都是基于async-validator的数据验证实现,如elementui、ant-design-vue、iview等

async-validator 基本使用

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

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

import AsyncValidator from 'async-validator';

// 1.声明规则 descriptor

const descriptor = {

name: [

{

type: 'string',

required: true,

message: 'name 字段不能为空!'

},

// 通过调用callback, 传递验证是否通过的结果

{

validator: (rule, value, callback) => {

if(value === 'muji1') {

returncallback('name 不能等于 muji1');

}

returncallback();

}

},

// 通过返回Error实例, 表示验证不通过

{

validator: (rule, value) => {

if(value === 'muji2') {

returnnewError('name 不能等于 muji2');

}

returntrue;

}

},

// 通过返回Promise实例, 传递验证是否通过的结果

{

validator: (rule, value) => {

returnnewPromise((resole, reject) => {

if(value === 'muji3') {

returnreject('name 不能等于 muji3');

}

returnresole();

})

}

}

],

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的构造函数做了什么事情。

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

constructor(descriptor: Rules) {

this.define(descriptor);

}

define(rules: Rules) {

// 规则配置不能为空

if(!rules) {

thrownewError('Cannot configure a schema with no rules');

}

// 规则配置必须是对象

if(typeofrules !== 'object'|| Array.isArray(rules)) {

thrownewError('Rules must be an object');

}

this.rules = {};

// 统一字段的规则配置方式为数组形式。因为给字段配置验证规则时, 可存在对象、数组的配置方式(如下).

/** 为name配置单条规则

* const descriptor = {

name: {

type: 'string',

required: true

}

}

也可以数组形式为name配置多条规则

const descriptor = {

name: [

{

type: 'string',

required: true

},

{

type: 'string',

validator: (rule, value) => value === 'muji'

}

]

}

*/

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方法

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

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

/** validate方法可接受三个参数

* source: 需要验证的数据源.

* options:验证参数(可选)

* callback:验证完成回调(可选。validate会返回promise,因此可直接通过promise执行验证完成后的逻辑)

*/

validate(source: Values, options: any = {}, callback: any = () => {}): Promise<Values> {

/**

* 第一步

* 处理传入参数。

* 如果options为函数, 则将该函数设置为完成回调,使第二个参数可直接传递callback, 方便调用者使用。

*/

if(typeofoptions === 'function') {

callback = options;

options = {};

}

// 此处省略了部分非核心逻辑代码

// series保存最终的数据验证的规则集合。

const series: Record<string, RuleValuePackage[]> = {};

/**

* 第二步

* 遍历、处理rules中所有字段的验证规则。(rules为构造函数中处理的处理保存的数据)

*/

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,

});

});

});

/**

* 第三步

* 遍历series的验证规则,执行每条规则的validator验证函数进行数据的验证。

* 然后asyncMap返回Promise. 可监听数据验证结果

*/

returnasyncMap(

series,

options,

(data, next) => {

// 每个规则的遍历回调。处理每条规则,并且执行规则中的验证函数validator (下面分析函数内具体逻辑)

},

errors => {

// 完成回调。所有规则处理完成后执行的回调

},

source,

);

}

getValidationMethod 获取规则的数据验证函数源码

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

getValidationMethod(rule: InternalRuleItem) {

// 存在自定义验证函数直接返回

if(typeofrule.validator === 'function') {

returnrule.validator;

}

// 省略部分非核心逻辑代码

// 根据指定的类型,获取对应的数据验证函数

returnvalidators[this.getType(rule)] || undefined;

}

// 根据规则配置项的配置,返回不同的类型

getType(rule: InternalRuleItem) {

// 不存在type类型, pattern为正则,则使用pattern类型

if(rule.type === undefined && rule.pattern instanceofRegExp) {

rule.type = 'pattern';

}

// 省略部分非核心逻辑代码

// 规则配置项中存在type则返回, 不存在则返回string类型

returnrule.type || 'string';

}

// async-validator中内置的数据类型。

varvalidators = {

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函数中进行验证。

第三步完整代码

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

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

/**

* 遍历series的验证规则,执行每条规则的validator验证函数进行数据的验证。

* 然后asyncMap返回Promise. 可监听数据验证结果

*/

returnasyncMap(

series,

options,

(data, next) => {

const rule = data.rule;

// 此处省略部分非核心逻辑

let res: ValidateResult;

/**

* 第一步

* 存在validator, 执行validator验证函数,不同数据类型的validator验证函数,对数据的验证逻辑不同

*/

if(rule.validator) {

/**

* 执行validator验证器函数

* rule: 规则

* data.value:需要验证的值

* cb:validator执行完成后, 通过cb函数处理验证的结果,然后执行下一个规则的验证

* data.source:原始传入的数据源

* options: 调用validate时传递的options

*/

res = rule.validator(rule, data.value, cb, data.source, options);

}

/**

* 第二步, 处理validator验证的返回结果, validator函数内部可以执行传递的cb函数传递验证的结果

*/

if(res === true) {

// validator返回true时,表示没有错误,直接执行cb进行下一个规则的验证。

cb();

} elseif(res instanceofError) {

// validator返回Error时, 传递错误信息给cb函数, cb函数记录错误信息, 然后cb函数执行下一个规则的验证

cb(res.message);

} elseif(res && (res as Promise<void>).then) {

/**

* validator验证函数中,亦可通过返回Promise传递验证的结果

* validator返回Promise时, 注册Promise的成功、失败回调

* 成功时:执行cb函数, 传递空, 表示不存在错误, 然后cb函数执行下一个规则

* 失败时: 执行cb函数, 传递错误信息, 然后cb函数执行下一个规则

*/

(res as Promise<void>).then(

() => cb(),

e => cb(e),

);

}

/**

* validator验证函数验证完成后,需要执行cb函数,进行验证结果的处理、记录

* 并调用next使asyncMap执行下一个规则的验证

*/

functioncb(e: SyncErrorType | SyncErrorType[] = []) {

let errorList = Array.isArray(e) ? e : [e];

if(errorList.length && rule.message !== undefined) {

errorList = [].concat(rule.message);

}

/**

* complementError中会为错误信息项填充额外的信息。如出现错误的字段、出现错误的值

*/

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数据验证器。

1

2

3

4

5

6

7

8

9

10

static functionregister(type: string, validator) {

if(typeofvalidator !== 'function') {

thrownewError(

'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的一点理解,希望我们能一起学习、一起进步。

;