ivew form表单实现思路整理

669 阅读1分钟

1.问题

  1. validate是怎么触发验证的?是在form上校验的还是在form-item上检验的?
  2. 为什么rules里面的trigger可以触发相应的校验
  3. 为什么自定义组件如i-input可以使用v-model指定

2.思维图(组件关系)

企业微信截图_16431703104300.png (丑一点别介意)

3.源码分析

文件:src\components\form\form.vue

props: {
    model: {
        type: Object
    },
    // 接收校验规则
    rules: {
        type: Object
    }
},
provide () {
// 将当前组件传递给子孙组件
    return { FormInstance : this };
},
methods: {
    // 校验方法
    validate(callback) {
        return new Promise(resolve => {
            let valid = true;
            let count = 0;
            // fields 为空需要返回promise
            if (this.fields.length === 0) {
                resolve(valid);
                if (typeof callback === 'function') {
                    callback(valid);
                }
            }
            // 遍历fields校验,相当于调用每一个form-item上的校验方法,都通过则通过,否则不通过
            this.fields.forEach(field => {
                field.validate('', errors => {
                    if (errors) {
                        valid = false;
                    }
                    if (++count === this.fields.length) {
                        resolve(valid);
                        if (typeof callback === 'function') {
                            callback(valid);
                        }
                    }
                });
            });
        });
    }
},
created () {
// 监听form派发的on-form-item-add事件,将其添加到自己的fields里面
    this.$on('on-form-item-add', (field) => {
        if (field) this.fields.push(field);
        return false;
    });
}

可以看到form里面主要做了3件事:

  1. 接收校验规则rules
  2. 将当前组件传递给子孙组件(这里主要方便后续form-item从form的rules里面获取自己对应的规则)
  3. 将form-item绑定到自己fields上,方便校验的时候遍历每一个form-item的校验器
  4. 添加校验器,供用户确定表单的时候调用(其实调用的是form-item的校验器,解决第一个问题

文件: src\components\form\form-item.vue

props: {
    // 通过prop从form的rules上取校验规则
    prop: {
        type: String
    },
    required: {
        type: Boolean,
        default: false
    },
    rules: {
        type: [Object, Array]
    }
},
// 获得form组件
inject: ['FormInstance'],
mounted () {
    if (this.prop) {
        // 派发on-form-item-add方法,将自己传递出去,form通过on-form-item-add监听
        this.dispatch('iForm', 'on-form-item-add', this);
        // 给当前子组件绑定规则,并监听插槽组件派发的事件,给当前组件在相应的事件触发绑定校验方法
        this.setRules();
    }
},
methods: {
    setRules() {
        // 监听插槽组件如input派发的事件,如blur,change,并调用自己的检验方法
        this.$on('on-form-blur', this.onFieldBlur);
        this.$on('on-form-change', this.onFieldChange);
    },
    
    getRules () {
       // 从form上获取rules
        let formRules = this.FormInstance.rules;
        const selfRules = this.rules;
        formRules = formRules ? formRules[this.prop] : [];
        return [].concat(selfRules || formRules || []);
    },
    
    // 筛选相应类型的校验规则,如trigger为blur或者为changede
    getFilteredRule (trigger) {
        const rules = this.getRules();
        return rules.filter(rule => !rule.trigger || rule.trigger.indexOf(trigger) !== -1);
    },
    // 添加校验器,trigger表示要校验的规则类型,没有则校验全部
    validate(trigger, callback = function () {}) {
        let rules = this.getFilteredRule(trigger);
        if (!rules || rules.length === 0) {
            // ...
        }
        let descriptor = {};
        descriptor[this.prop] = rules;
        // 检验器,调用三方库async-validator
        const validator = new AsyncValidator(descriptor);
        let model = {};
        model[this.prop] = this.fieldValue;

        validator.validate(model, { firstFields: true }, errors => {
            this.validateMessage = errors ? errors[0].message : '';

            callback(this.validateMessage);
        });
    },
    onFieldBlur() {
        // 插槽组件失去焦点触发事件,校验trigger为blur的规则
        this.validate('blur');
    },
    onFieldChange() {
    // 插槽组件改变触发事件,校验trigger为change的规则
        this.validate('change');
    }
}
  1. 接收prop、rules等
  2. inject获得form
  3. mounted里面派发on-form-item-add方法,将自己传递出去,form通过on-form-item-add监听
  4. 监听插槽组件如input派发的事件,如blur,change,并调用自己的检验方法:如:this.validate('blur')(解决第二个问题
  5. 添加校验器validate(trigger,callback),根据参数trigger获取相应规则,调用三方库async-validator检验

文件:src\components\input\input.vue

<template>
  <input
      :value="currentValue"
      @blur="handleBlur"
      @input="handleInput"
      @change="handleChange">
</template>
methods: {
  handleBlur (event) {
    this.$emit('on-blur', event);
    if (!findComponentUpward(this, ['DatePicker', 'TimePicker', 'Cascader', 'Search'])) {
        // 派发on-form-blur事件,供form-item监听,触发相应校验
        this.dispatch('FormItem', 'on-form-blur', this.currentValue);
    }
  },
  handleInput (event) {
    // ...
    let value = event.target.value;
    if (this.number && value !== '') value = Number.isNaN(Number(value)) ? value : Number(value);
    // 这里主要是为了让input这个自定义组件可以实现v-model的双向绑定
    this.$emit('input', value);
    // ...
  },
}
  1. 失去焦点的时候派发on-form-blur事件,供form-item监听,触发相应校验(解决第二个问题) 第三个问题参照官网 (以上为自己的学习记录,如有错误,欢迎指正)