async-validator 源码学习笔记(五):Schema

514 阅读4分钟

系列文章:

系列文章

1、async-validator 源码学习(一):文档翻译

2、async-validator 源码学习笔记(二):目录结构

3、async-validator 源码学习笔记(三):rule

4、async-validator 源码学习笔记(四):validator

Schema 是 async-validator 库的标准使用方式,使用 class 类的形式和构造函数方式进行定义的。

一、Schema 类使用方式

在官方文档中 Schema 的使用方式有如下几步:

  1. 从 async-validator 中引入 Schema 类。
  2. 定义校验规则 descriptor 。
  3. 使用 new 创建一个 Schema 的实例。
  4. 调用实例的 validate 方法。

使用 demo:

//引入
import Schema from 'async-validator'
// 定义校验规则
const descriptor  = {
 username: {
  type: 'string',
  required: true,
  validator(rule, value) {
   return value != ''
  },
  message: '用户名不能为空',
 }
}
//实例化
const validator = new Schema(descriptor )
//开始校验
validator.validate({ username:'需要验证的值' },(error, fields) => {
 if(errors){
  //校验失败
   return handleError( errors, fields )
 }
 //校验成功 
})
// 或者 promise 用法
validator.validate({ username:'需要验证的值' })
.then(res => {
 //校验成功
})
.catch(({errors,fields}) => {
//校验失败
})

new Schema(descriptor ) 直接开始实例化了,我们看看 Schema 是如何定义的?

二、Schema 源码分析

Schema 的构造函数:主要分为三步:

1、this 上定义实例属性 rules 为 null。

// 构造函数
var Schema = function () {
  function Schema(descriptor) {
    // 1、实例属性 rules 默认值为空 
    this.rules = null;
    // 2、私有属性 _messages 设置初始化值
    this._messages = messages;
    // 3、正式开始构建实例
    this.define(descriptor);
  }
  return Schema;
}();
Schema.warning = warning;
Schema.messages = messages;
Schema.validators = validators;

exports['default'] = Schema;

2、this 上定义一个实例私有属性 _message ,初始化值为:

function newMessages() {
  ...
}
var messages = newMessages();

3、调用原型链 define 方法,传参 descriptor 。

descriptor 是实例化时传入的校验规则。

由上可以看到,Schema 原型链上 添加了 register、warning、messages、validators 四个方法。

2.1、warning

在实例化 Schema 之前设置 warning 方法,只要 warning 方法设置为一个空函数就能够屏蔽控制台警告。

Schema.warning = () => {}
// don't print warning message when in production env or node runtime

可以发现 warning 本身就被设为了一个空函数,只有在开发环境或非 node运行时,才会用 console.warn 打印出 errors 数组中的每一个 error。

var warning = function warning() {}; 
// don't print warning message when in production env or node runtime

if (typeof process !== 'undefined' && process.env && process.env.NODE_ENV !== 'production' && typeof window !== 'undefined' && typeof document !== 'undefined') {
  warning = function warning(type, errors) {
    if (typeof console !== 'undefined' && console.warn && typeof ASYNC_VALIDATOR_NO_WARNING === 'undefined') {
      if (errors.every(function (e) {
        return typeof e === 'string';
      })) {
        console.warn(type, errors);
      }
    }
  };
}

2.2、message

Schema 中的 message 方法实质是根据不同类型校验失败后错误提示使用的提示信息模板。官方提供了一个默认的模板:

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: function clone() {
      var cloned = JSON.parse(JSON.stringify(this));
      cloned.clone = this.clone;
      return cloned;
    }
  };
}
var messages = newMessages();

上述是官方提供的默认模板提示信息,还可以根据自己的项目进行定制化,因为官方也提供了 deepMerge 方法:

_proto.messages = function messages(_messages) {
    if (_messages) {
      this._messages = deepMerge(newMessages(), _messages);
    }
    return this._messages;
  };
/* 深度合并 */
function deepMerge(target, source) {
  if (source) {
    // 更新 message
    for (var s in source) {
      if (source.hasOwnProperty(s)) {
        var value = source[s];

        if (typeof value === 'object' && typeof target[s] === 'object') {
          target[s] = _extends({}, target[s], value);
        } else {
          target[s] = value;
        }
      }
    }
  }
  return target;
}

所以可以根据自己项目需求,可以自己定制化一个 message 校验提示。

2.3、validators

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
};

以对string类型的判断为例

rule: 在源descriptor中,与要校验的字段名称相对应的校验规则。始终为它分配一个field属性,其中包含要验证的字段的名称。

//这个样子
{
    [field: string]: RuleItem | RuleItem[]
}
//例子
{name:{type: "string", required: true, message: "Name is required"}}

2.4、register

除了 validators 方法,还提供了 register 方法,用于新增类型校验器:

Schema.register = function register(type, validator) {
  // validator 必须是函数
  if (typeof validator !== 'function') {
    //无法按类型注册验证程序,验证程序不是函数
    throw new Error('Cannot register a validator by type, validator is not a function');
  }
  
  validators[type] = validator;
};