欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

async-validator實(shí)現(xiàn)原理源碼解析

 更新時(shí)間:2023年01月11日 11:26:23   作者:會(huì)說話的番茄  
這篇文章主要為大家介紹了async-validator實(shí)現(xiàn)原理源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

async-validator 介紹

async-validator是異步的驗(yàn)證數(shù)據(jù)是否合法有效的工具, 內(nèi)置了不同數(shù)據(jù)類型的常見驗(yàn)證規(guī)則。

在需要對數(shù)據(jù)進(jìn)行驗(yàn)證的場景中,都可以考慮使用async-validator。 很多流行的組件庫的表單驗(yàn)證都是基于async-validator的數(shù)據(jù)驗(yàn)證實(shí)現(xiàn),如elementui、ant-design-vue、iview等

async-validator 基本使用

import AsyncValidator from 'async-validator';
// 1.聲明規(guī)則 descriptor
const descriptor = {
  name: [
    {
      type: 'string',
      required: true,
      message: 'name 字段不能為空!'
    },
    // 通過調(diào)用callback, 傳遞驗(yàn)證是否通過的結(jié)果
    {
      validator: (rule, value, callback) => {
        if (value === 'muji1') {
          return callback('name 不能等于 muji1');
        }
        return callback();
      }
    },
    // 通過返回Error實(shí)例, 表示驗(yàn)證不通過
    {
      validator: (rule, value) => {
        if (value === 'muji2') {
          return new Error('name 不能等于 muji2');
        }
        return true;
      }
    },
    // 通過返回Promise實(shí)例, 傳遞驗(yàn)證是否通過的結(jié)果
    {
      validator: (rule, value) => {
        return new Promise((resole, reject) => {
          if (value === 'muji3') {
            return reject('name 不能等于 muji3');
          }
          return resole();
        })
      }
    }
  ],
  age: {
    type: 'number',
    // 自定義驗(yàn)證規(guī)則. age字段不能小于18, 小于則提示 ‘年紀(jì)太小'
    validator: (rule, value, callback) => {
        if (value < 18) {
          // 通過執(zhí)行callback傳遞數(shù)據(jù)驗(yàn)證的結(jié)果
          callback('年紀(jì)太小');
        } else {
          callback();
        }
    },
  },
};
// 2.創(chuàng)建async-validator實(shí)例
const validator = new AsyncValidator(descriptor);
// 3.數(shù)據(jù)源
const data = { name: 'muji', age: 16 }
// 4.執(zhí)行驗(yàn)證
validator.validate(data, function(errors, fields) {
    if (!errors) {
        // 驗(yàn)證成功
        console.log('驗(yàn)證通過');
    } else {
        // 驗(yàn)證失敗
        console.log('驗(yàn)證不通過', error);
    }
})

async-validator 源碼分析

從上面的基本使用中可以看到, 使用async-validator的過程主要分為:

1.創(chuàng)建async-validator實(shí)例

2.執(zhí)行實(shí)例的validate方法時(shí),傳入數(shù)據(jù)源進(jìn)行驗(yàn)證

async-validator 源碼-構(gòu)造函數(shù)

我們先分析第一步創(chuàng)建async-validator實(shí)例時(shí),async-validator的構(gòu)造函數(shù)做了什么事情。

constructor(descriptor: Rules) {
    this.define(descriptor);
}
define(rules: Rules) {
    // 規(guī)則配置不能為空
    if (!rules) {
      throw new Error('Cannot configure a schema with no rules');
    }
    // 規(guī)則配置必須是對象
    if (typeof rules !== 'object' || Array.isArray(rules)) {
      throw new Error('Rules must be an object');
    }
    this.rules = {};
    // 統(tǒng)一字段的規(guī)則配置方式為數(shù)組形式。因?yàn)榻o字段配置驗(yàn)證規(guī)則時(shí), 可存在對象、數(shù)組的配置方式(如下). 
    /** 為name配置單條規(guī)則
    * const descriptor = {
          name: {
            type: 'string',
            required: true
          }
      }
      也可以數(shù)組形式為name配置多條規(guī)則
      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];
    });
}

構(gòu)造函數(shù)中只是執(zhí)行了define方法。

而define方法內(nèi)做了以下幾步:

1.驗(yàn)證傳入的驗(yàn)證規(guī)則是否合法。

2.統(tǒng)一字段的規(guī)則配置方式為數(shù)組形式

async-validator 源碼-validate方法

/** validate方法可接受三個(gè)參數(shù)
 * source: 需要驗(yàn)證的數(shù)據(jù)源.
 * options:驗(yàn)證參數(shù)(可選)
 * callback:驗(yàn)證完成回調(diào)(可選。validate會(huì)返回promise,因此可直接通過promise執(zhí)行驗(yàn)證完成后的邏輯)
 */ 
validate(source: Values, options: any = {}, callback: any = () => {}): Promise<Values> {
    /**
     * 第一步
     * 處理傳入?yún)?shù)。
     * 如果options為函數(shù), 則將該函數(shù)設(shè)置為完成回調(diào),使第二個(gè)參數(shù)可直接傳遞callback, 方便調(diào)用者使用。
    */
    if (typeof options === 'function') {
      callback = options;
      options = {};
    }
    // 此處省略了部分非核心邏輯代碼
    // series保存最終的數(shù)據(jù)驗(yàn)證的規(guī)則集合。
    const series: Record<string, RuleValuePackage[]> = {};
    /**
     * 第二步
     * 遍歷、處理rules中所有字段的驗(yàn)證規(guī)則。(rules為構(gòu)造函數(shù)中處理的處理保存的數(shù)據(jù))
    */
    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驗(yàn)證器函數(shù)(每個(gè)規(guī)則都必須存在一個(gè)validator函數(shù)去處理數(shù)據(jù)的驗(yàn)證邏輯)
        // getValidationMethod就是獲取該rule的validator驗(yàn)證函數(shù)。
        // 如果rule中存在自定義的validator配置,則直接返回。
        // 如果不存在,則嘗試根據(jù)rule中的type數(shù)據(jù)類型獲取內(nèi)置的validator驗(yàn)證函數(shù)
        rule.validator = this.getValidationMethod(rule);
        if (!rule.validator) {
          return;
        }
        // 為rule補(bǔ)充字段、類型的信息
        rule.field = field;
        rule.fullField = rule.fullField || field;
        // 處理完rule后, 將該rule添加到series中
        series[field] = series[field] || [];
        series[field].push({
          rule,
          value,
          source,
          field: field,
        });
      });
    });
    /**
     * 第三步
     * 遍歷series的驗(yàn)證規(guī)則,執(zhí)行每條規(guī)則的validator驗(yàn)證函數(shù)進(jìn)行數(shù)據(jù)的驗(yàn)證。
     * 然后asyncMap返回Promise. 可監(jiān)聽數(shù)據(jù)驗(yàn)證結(jié)果
    */
    return asyncMap(
      series,
      options,
      (data, next) => {
        // 每個(gè)規(guī)則的遍歷回調(diào)。處理每條規(guī)則,并且執(zhí)行規(guī)則中的驗(yàn)證函數(shù)validator (下面分析函數(shù)內(nèi)具體邏輯)
      },
      errors => {
        // 完成回調(diào)。所有規(guī)則處理完成后執(zhí)行的回調(diào)
      },
      source,
    );
  }

getValidationMethod 獲取規(guī)則的數(shù)據(jù)驗(yàn)證函數(shù)源碼

getValidationMethod(rule: InternalRuleItem) {
    // 存在自定義驗(yàn)證函數(shù)直接返回
    if (typeof rule.validator === 'function') {
      return rule.validator;
    }
    // 省略部分非核心邏輯代碼
    // 根據(jù)指定的類型,獲取對應(yīng)的數(shù)據(jù)驗(yàn)證函數(shù)
    return validators[this.getType(rule)] || undefined;
}
// 根據(jù)規(guī)則配置項(xiàng)的配置,返回不同的類型
getType(rule: InternalRuleItem) {
    // 不存在type類型, pattern為正則,則使用pattern類型
    if (rule.type === undefined && rule.pattern instanceof RegExp) {
      rule.type = 'pattern';
    }
    // 省略部分非核心邏輯代碼
    // 規(guī)則配置項(xiàng)中存在type則返回, 不存在則返回string類型
    return rule.type || 'string';
}
// async-validator中內(nèi)置的數(shù)據(jù)類型。
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
};

第二步中的getValidationMethod方法,為每個(gè)rule驗(yàn)證規(guī)則獲取具體驗(yàn)證數(shù)據(jù)的validator驗(yàn)證函數(shù)。

到第三步后,遍歷驗(yàn)證規(guī)則series集合,執(zhí)行規(guī)則中的validator驗(yàn)證函數(shù)時(shí),把數(shù)據(jù)傳入validator函數(shù)中進(jìn)行驗(yàn)證。

第三步完整代碼

/**
  * 遍歷series的驗(yàn)證規(guī)則,執(zhí)行每條規(guī)則的validator驗(yàn)證函數(shù)進(jìn)行數(shù)據(jù)的驗(yàn)證。
  * 然后asyncMap返回Promise. 可監(jiān)聽數(shù)據(jù)驗(yàn)證結(jié)果
 */
 return asyncMap(
   series,
   options,
   (data, next) => {
       const rule = data.rule;
       // 此處省略部分非核心邏輯
       let res: ValidateResult;
       /**
       * 第一步
       * 存在validator, 執(zhí)行validator驗(yàn)證函數(shù),不同數(shù)據(jù)類型的validator驗(yàn)證函數(shù),對數(shù)據(jù)的驗(yàn)證邏輯不同
       */ 
       if (rule.validator) {
          /**
           * 執(zhí)行validator驗(yàn)證器函數(shù)
           * rule: 規(guī)則
           * data.value:需要驗(yàn)證的值
           * cb:validator執(zhí)行完成后, 通過cb函數(shù)處理驗(yàn)證的結(jié)果,然后執(zhí)行下一個(gè)規(guī)則的驗(yàn)證
           * data.source:原始傳入的數(shù)據(jù)源
           * options: 調(diào)用validate時(shí)傳遞的options
          */ 
          res = rule.validator(rule, data.value, cb, data.source, options);
        }
        /**
        * 第二步, 處理validator驗(yàn)證的返回結(jié)果, validator函數(shù)內(nèi)部可以執(zhí)行傳遞的cb函數(shù)傳遞驗(yàn)證的結(jié)果
        */
        if (res === true) {
          // validator返回true時(shí),表示沒有錯(cuò)誤,直接執(zhí)行cb進(jìn)行下一個(gè)規(guī)則的驗(yàn)證。
          cb();
        } else if (res instanceof Error) {
           // validator返回Error時(shí), 傳遞錯(cuò)誤信息給cb函數(shù), cb函數(shù)記錄錯(cuò)誤信息, 然后cb函數(shù)執(zhí)行下一個(gè)規(guī)則的驗(yàn)證
          cb(res.message);
        } else if (res && (res as Promise<void>).then) {
          /**
           * validator驗(yàn)證函數(shù)中,亦可通過返回Promise傳遞驗(yàn)證的結(jié)果
           * validator返回Promise時(shí), 注冊Promise的成功、失敗回調(diào)
           * 成功時(shí):執(zhí)行cb函數(shù), 傳遞空, 表示不存在錯(cuò)誤, 然后cb函數(shù)執(zhí)行下一個(gè)規(guī)則
           * 失敗時(shí): 執(zhí)行cb函數(shù), 傳遞錯(cuò)誤信息, 然后cb函數(shù)執(zhí)行下一個(gè)規(guī)則
          */
          (res as Promise<void>).then(
            () => cb(),
            e => cb(e),
          );
        }
        /**
        * validator驗(yàn)證函數(shù)驗(yàn)證完成后,需要執(zhí)行cb函數(shù),進(jìn)行驗(yàn)證結(jié)果的處理、記錄
        * 并調(diào)用next使asyncMap執(zhí)行下一個(gè)規(guī)則的驗(yàn)證
        */
        function cb(e: SyncErrorType | SyncErrorType[] = []) {
          let errorList = Array.isArray(e) ? e : [e];
          if (errorList.length && rule.message !== undefined) {
            errorList = [].concat(rule.message);
          }
          /**
           * complementError中會(huì)為錯(cuò)誤信息項(xiàng)填充額外的信息。如出現(xiàn)錯(cuò)誤的字段、出現(xiàn)錯(cuò)誤的值
           */ 
          let filledErrors = errorList.map(complementError(rule, source));
          // asyncMap并不是同步循環(huán)series規(guī)則集合,而是遍歷的過程中,需要等待執(zhí)行next才會(huì)遍歷下一個(gè)series中的規(guī)則
          // 將錯(cuò)誤結(jié)果filledErrors傳遞到下一個(gè)規(guī)則的事件循環(huán)中,最后所有規(guī)則驗(yàn)證完成時(shí),能夠獲取到所有的規(guī)則的驗(yàn)證結(jié)果
          next(filledErrors);
        }
   },
   // errors 即所有驗(yàn)證不通過的錯(cuò)誤記錄(即執(zhí)行next時(shí)傳遞的所有filledErrors)
   errors => {
        // 所有規(guī)則處理完成后執(zhí)行的回調(diào)
        let fields: ValidateFieldsError = {};
        if (!errors.length) {
          // 不存在錯(cuò)誤, 直接執(zhí)行validate時(shí)傳遞的完成回調(diào)
          callback(null, source);
        } else {
          // 存在錯(cuò)誤
          // 將errors錯(cuò)誤記錄按字段分類, 如每個(gè)字段可配置多條規(guī)則, 因此每個(gè)字段可能存在多個(gè)錯(cuò)誤記錄
          // fields 數(shù)據(jù)格式如 { field1: [error1, error2], field2: [error1] }
          fields = convertFieldsError(errors);
          // 執(zhí)行完成回調(diào), 傳遞errors錯(cuò)誤記錄, fields錯(cuò)誤記錄分類
          (callback as (
            errors: ValidateError[],
            fields: ValidateFieldsError,
          ) => void)(errors, fields);
        }
   },
   source,
 );

以上代碼主要分為以下幾步:

1.遍歷驗(yàn)證的規(guī)則集合

2.執(zhí)行每條規(guī)則的validator驗(yàn)證函數(shù),進(jìn)行數(shù)據(jù)驗(yàn)證。

3.驗(yàn)證完成后, 執(zhí)行cb函數(shù)處理、記錄驗(yàn)證的結(jié)果,然后cb執(zhí)行next處理下一條規(guī)則。

4.所有規(guī)則遍歷處理完后,觸發(fā)調(diào)用validate時(shí)傳入的callback,并傳入驗(yàn)證結(jié)果。

async-validator 源碼-register方法

在validators中注冊新的validator數(shù)據(jù)驗(yàn)證器。

static function register(type: string, validator) {
    if (typeof validator !== 'function') {
      throw new Error(
        'Cannot register a validator by type, validator is not a function',
      );
    }
    // 將該type的validator數(shù)據(jù)驗(yàn)證器函數(shù)添加到validators中
    // 后續(xù)執(zhí)行數(shù)據(jù)驗(yàn)證時(shí),會(huì)根據(jù)type在validators中取驗(yàn)證器對數(shù)據(jù)進(jìn)行驗(yàn)證
    validators[type] = validator;
};

總結(jié)

async-validator可以分為兩個(gè)部分。
1.validators驗(yàn)證器集合: 保存著不同type數(shù)據(jù)類型的驗(yàn)證函數(shù)??梢酝ㄟ^register對validators進(jìn)行擴(kuò)展。

2.validate方法: 為rule規(guī)則根據(jù)type數(shù)據(jù)類型在validators驗(yàn)證器集合中匹配對應(yīng)的validator函數(shù)進(jìn)行數(shù)據(jù)驗(yàn)證。大致的執(zhí)行過程如下

  • 遍歷外部傳入的規(guī)則配置項(xiàng),根據(jù)配置項(xiàng)中的type數(shù)據(jù)類型,獲取對應(yīng)數(shù)據(jù)類型的validator驗(yàn)證函數(shù),得到最終的驗(yàn)證規(guī)則集合。
  • 遍歷最終的規(guī)則集合,執(zhí)行規(guī)則的validator驗(yàn)證函數(shù), 處理、收集驗(yàn)證函數(shù)的驗(yàn)證結(jié)果。
  • 所有規(guī)則執(zhí)行完成后,觸發(fā)外部傳遞的callback完成函數(shù),并且傳遞收集到的驗(yàn)證結(jié)果。

最后

async-validator中非核心流程的部分經(jīng)過了省略。

以上只是我對async-validator的一點(diǎn)理解,希望我們能一起學(xué)習(xí)、一起進(jìn)步。

最后,你可以從功能的實(shí)現(xiàn)、代碼的組織、可讀性等任何的角度思考下async-validator中做得比較好或者能夠優(yōu)化的地方嗎?更多關(guān)于async-validator原理的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論