剖析 b-validate 实现原理

一、前言

  b-validate 是一个小巧且功能丰富的类型校验库,它的特点如下:

  • 链式调用

  • 支持自定义校验规则

  • 支持 schema 校验

  • 支持异步校验

  它基础的使用方式如下:

bv(123)
  .number
  .min(2)
  .max(10)
  .collect((error) => {
    console.log(error); // { value: 123, type: 'number', message: '123 is not less than 10' }
    // if no error, error equal to null
  });

const error = bv('b-validate').string.isRequired.match(/validater/).end;
// { value: 'b-validate', type: 'string', message: '`b-validate` is not match pattern /validater/' }

二、Base Class

  b-validate 所有数据结构的校验类是基于 Base Class 来扩展:

class Base {
  constructor(obj, options) {
    if (isObject(options) && isString(obj) && options.trim) {
      this.obj = obj.trim();
    } else if (isObject(options) && options.ignoreEmptyString && obj === "") {
      this.obj = undefined;
    } else {
      this.obj = obj;
    }
    // 自定义校验提示信息
    this.message = options.message;
    this.type = options.type;
    // 存储校验结果
    this.error = null;
  }

  get end() {
    return this.error;
  }

  addError(message) {
    if (!this.error && message) {
      this.error = {
        value: this.obj,
        type: this.type,
        message: this.message || `${this._not ? "[NOT MODE]:" : ""}${message}`,
      };
    }
  }

  validate(expression, errorMessage) {
    const _expression = this._not ? expression : !expression;
    if (_expression) {
      this.addError(errorMessage);
    }
    return this;
  }

  collect(callback) {
    callback && callback(this.error);
  }
}

  校验流程非常的容易理解,就是「通过调用各种规则输出最终的 this.error 信息」

// 省略部分代码
class ArrayValidater extends Base {
  constructor(obj, options) {
    super(obj, {
      ...options,
      type: 'array'
    });
    this.validate(
      options && options.strict ? isArray(this.obj) : true,
      `Expect array type but got \`${this.obj}\``
    );
  }

  length(num) {
    return this.obj ? this.validate(
      this.obj.length === num,
      `Expect array length ${num} but got ${this.obj.length}`
    ) : this;
  }
  
  minLength(num) {
    return this.obj ? this.validate(
      this.obj.length >= num,
      `Expect min array length ${num} but got ${this.obj.length}`
    ) : this;
  }
}

  针对于特定数据结构,在子类的构造函数中首先会校验其类型,然后就是扩展各种特定的校验规则方法。

三、链式调用

  b-validate 采用了最通常的方式实现链式调用:「基于 this 作用域」

// 省略部分代码
class Base {
  constructor(obj, options) {
  }

  get not() {
    this._not = !this._not;
    // 返回当前实例,以提供链式调用
    return this;
  }

  get isRequired() {
    if (isEmptyValue(this.obj) || isEmptyArray(this.obj)) {
      this.error = {
        value: this.obj,
        type: this.type,
        requiredError: true,
        message:
          this.message ||
          `${this._not ? "[NOT MODE]:" : ""}${this.type} is required`,
      };
    }
    // 返回当前实例,以提供链式调用
    return this;
  }
}

四、schema 实现

  schema 与之前调用方式的不同点在于:

  • 批量处理类型校验

  • 通过 schema 约定减少手动链式调用的繁琐

export class Schema {
  constructor(schema, options = {}) {
    this.schema = schema;
    this.options = options;
  }

  validate(values, callback) {
    if (!isObject(values)) {
      return;
    }
    const promises = [];
    let errors = null;
    // 根据 key 设置相应的 error 信息
    function setError(key, error) {
      if (!errors) {
        errors = {};
      }
      if (!errors[key] || error.requiredError) {
        errors[key] = error;
      }
    }
    if (this.schema) {
      Object.keys(this.schema).forEach((key) => {
        if (isArray(this.schema[key])) {
          for (let i = 0; i < this.schema[key].length; i++) {
            const rule = this.schema[key][i];
            const type = rule.type;
            const message = rule.message;
            // 省略部分代码
          }
        }
      });
    }
    // 省略部分代码
    callback && callback(errors);
  }
}

  在有 schema 约定的前提下,内部执行代码可以自动遍历每一条规则,通过 collect 方法获取到相应的 this.error 信息,最终调用 setError 方法整合成数组输出。

五、总结

  本文重点如下:

  • 了解类型校验的核心流程

  • 基于继承来扩展不同数据结构的校验规则类

  • 基于 this 作用域实现链式调用

  • 通过 schema 的设计优化批量数据校验场景下的校验流程

  对于数据类型校验库感兴趣的同学,千万不要错过 「joi」 这个库。

  最后,「如果本文对您有帮助,欢迎点赞、收藏、分享」

231c47b2253a5e37b9dfea85d2c4e79a.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要使用Vuelidate或vee-validate来限制字符串长度,您需要按照以下步骤进行操作: 1. 安装Vuelidate或vee-validate 使用npm或yarn安装Vuelidate或vee-validate: ```bash # 使用npm安装Vuelidate npm install vuelidate # 使用yarn安装Vuelidate yarn add vuelidate # 使用npm安装vee-validate npm install vee-validate # 使用yarn安装vee-validate yarn add vee-validate ``` 2. 在Vue组件中引入Vuelidate或vee-validate 在Vue组件中引入Vuelidate或vee-validate,并在组件的`data`选项中添加表单数据: ```javascript import { required, maxLength } from 'vuelidate/lib/validators' import { ValidationProvider, ValidationObserver } from 'vee-validate' export default { data() { return { form: { name: '', email: '', message: '' } } }, components: { ValidationProvider, ValidationObserver }, validations: { form: { name: { required, maxLength: maxLength(50) // 设置最大长度为50 }, email: { required, email }, message: { maxLength: maxLength(200) // 设置最大长度为200 } } } } ``` 在上面的代码中,我们在Vue组件中引入了Vuelidate和vee-validate,并在`data`选项中添加了表单数据。我们还在`validations`选项中为每个表单字段添加了验证规则,其中包括最大长度。 3. 在模板中使用ValidationProvider和ValidationObserver 使用`ValidationProvider`和`ValidationObserver`组件来显示验证错误信息: ```html <template> <ValidationObserver> <div> <label for="name">Name:</label> <ValidationProvider rules="required|maxLength" v-slot="{ errors }"> <input id="name" v-model.trim="form.name" type="text"> <span class="error">{{ errors[0] }}</span> </ValidationProvider> </div> <div> <label for="email">Email:</label> <ValidationProvider rules="required|email" v-slot="{ errors }"> <input id="email" v-model.trim="form.email" type="email"> <span class="error">{{ errors[0] }}</span> </ValidationProvider> </div> <div> <label for="message">Message:</label> <ValidationProvider rules="maxLength" v-slot="{ errors }"> <textarea id="message" v-model.trim="form.message"></textarea> <span class="error">{{ errors[0] }}</span> </ValidationProvider> </div> </div> </ValidationObserver> </template> ``` 在上面的代码中,我们在每个表单字段的外部包装了`ValidationProvider`组件,并为其设置了验证规则。在每个验证提供程序中,我们使用`v-slot`指令来访问验证错误信息,并在模板中显示它们。 请注意,这只是一个简单的示例,您可以根据自己的需求进行更改和扩展。希望这些信息对您有所帮助!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值