返回

async-validator源码解析(二):rule

前端

在上篇 async-validator 源码解析(一):文档翻译 中,我们已经将 ElementUI 和 Ant Design 都依赖的 async-validator 校验库的文档进行了翻译,下面继续来填坑分析 async-validator 的源码,理解表单校验的原理。可以从仓库 https://github.com/yiminghe/async-validator 获取源码。

源码位置

packages/async-validator/src/rule.js

相关类

Field         校验的字段
Rule          具体的校验规则
FieldEntity   一个字段的所有规则组成的对象

字段

/**
 * 校验字段的具体校验规则
 *
 * @example
 * ```
 * const rule = {
 *   required: true,
 *   min: 10,
 *   max: 20,
 *   pattern: /^[a-zA-Z0-9]+$/
 * };
 * ```
 */
export class Rule {
  /**
   * 校验函数的返回结果
   *
   * @typedef {Object} RuleResult
   * @property {*} value 校验字段的实际值
   * @property {string} rule 校验字段的规则,key就是校验规则的名称,value是对应的校验规则的配置
   * @property {boolean} valid 是否符合校验规则,如果校验通过返回 `true`,否则返回 `false`
   * @property {string} message 错误提示信息,校验不通过的时候返回错误信息
   */

  /**
   * @param {string} name 校验规则的名称
   * @param {RuleOptions} [options] 校验规则的配置
   */
  constructor(name, options = {}) {
    this.name = name;
    this.message = options.message || FieldEntity.get(name, 'messages', '');
    this.trigger = options.trigger === null ? [] : options.trigger;
    this.triggerType = options.triggerType;
    this.exclusive = !!options.exclusive;
    this.validator = options.validator;
    this.validate = options.validate;
    this.async = !!options.async;
    this.params = options.params;
  }

  /**
   * 校验字段的值
   *
   * @param {*} value 校验字段的实际值
   * @param {string} [rule] 校验字段的规则,key就是校验规则的名称,value是对应的校验规则的配置
   * @param {FieldEntity} field 校验的字段
   * @param {string} [source] 字段校验时产生校验结果的源字段名
   * @param {boolean} [silent] 是否在校验不通过的情况下不抛出错误
   * @returns {RuleResult} 校验结果
   */
  validate(value, rule, field, source, silent) {
    let errors;
    let result;
    const { message, validator, validate, triggerType } = this;

    if (typeof rule === 'string') {
      result = {
        rule: rule,
        value,
        valid: true
      };
    } else {
      result = rule;
      result.value = value;
      if (field && triggerType === 'change' && !field.required) {
        result.valid = true;
      }
    }

    if (triggerType === 'change' && field && field.required && result.valid && value === field.initialValue) {
      return result;
    }

    if (this.async) {
      errors = validator
        ? Promise.resolve(this.validator(value, rule, field, source))
        : validate(value, rule, field, source, this.params);

      errors = errors.catch((err) => {
        if (err instanceof Error) {
          err.message = message;
        }
        return err;
      });
      return errors.then((errors) => {
        if (Array.isArray(errors)) {
          errors.forEach((err) => {
            err.field = source;
            err.message = message;
          });
          result.valid = false;
          return errors;
        } else {
          result.valid = !errors;
          return result;
        }
      }, (err) => {
        if (err instanceof Error) {
          err.message = message;
        }
        result.valid = false;
        return err;
      });
    }

    if (validator) {
      errors = this.validator(value, rule, field, source);
    } else if (validate) {
      errors = validate(value, rule, field, source, this.params);
    }

    if (errors === true) {
      return result;
    }

    if (Array.isArray(errors)) {
      errors.forEach((err) => {
        err.field = source;
        err.message = message;
      });
      result.valid = false;
      return errors;
    } else if (errors instanceof Error) {
      errors.message = message;
      result.valid = false;
      return errors;
    } else if (typeof errors === 'string') {
      result.valid = false;
      return errors;
    }

    return result;
  }
}

方法

/**
 * 校验给定字段
 *
 * @param {*} value 校验字段的实际值
 * @param {string} rule 校验字段的规则,key就是校验规则的名称,value是对应的校验规则的配置
 * @param {FieldEntity} field 校验的字段
 * @param {string} [source] 字段校验时产生校验结果的源字段名
 * @param {boolean} [silent] 是否在校验不通过的情况下不抛出错误
 * @returns {Promise<RuleResult | RuleResult[]>} 校验结果
 */
static validate(value, rule, field, source, silent) {
  if (Array.isArray(rule)) {
    return Promise.all(rule.map((subRule) => {
      const result = Rule.validate(value, subRule, field, source, silent);
      if (result instanceof Error) {
        throw result;
      }
      if (result.valid === false) {
        throw new Error(result.message);
      }
      return result;
    }));
  }
  if (rule instanceof Rule) {
    return rule.validate(value, rule, field, source, silent);
  }
  if (typeof rule === 'string') {
    rule = FieldEntity.get(rule);
  }
  return new Rule(rule).validate(value, rule, field, source, silent);
}

总结

async-validator 的 Rule 类是具体校验规则的封装,包含了校验函数、错误提示信息、触发时机等信息。validate 方法可以对给定字段进行校验,并返回校验结果。Rule 类是 async-validator 校验库的核心组成部分,也是理解表单校验原理的基础。