Чтение и анализ компонентов формы исходного кода Element

внешний интерфейс исходный код JavaScript Element Vue.js

Анализ исходного кода элемента из формы включает проверку таких компонентов, как ввод, выбор, флажок, средство выбора и радио. Проверка формы использует плагин async-validator. Давайте посмотрим вместе.

首先form表单组件由两部分组成

  • форма: Единое управление формой-поз.
  • form-item: отвечает за завершение проверки и т. д.

form

структура

  <form class="el-form" :class="[
    labelPosition ? 'el-form--label-' + labelPosition : '',
    { 'el-form--inline': inline }
  ]">
    <slot></slot>
  </form>

Структура очень проста, элемент формы оборачивает слот, который является элементом формы.

раздел сценария

1. Удобно ассоциировать с элементом формы и вводить экземпляр формы

    // 注入组件实例
    provide() {
      return {
        elForm: this
      };
    }

2. После создания экземпляра поля собирают экземпляр элемента формы.

    created() {
      // 监听el.form.addField事件,触发:将form-item实例push到fields
      this.$on('el.form.addField', (field) => {
        if (field) {
          this.fields.push(field);
        }
      });
      // 监听el.form.removeField事件,触发:form-item实例有prop规则属性从fields移除form-item实例
      this.$on('el.form.removeField', (field) => {
        if (field.prop) {
          this.fields.splice(this.fields.indexOf(field), 1);
        }
      });
    }

3. Некоторые методы проверки формы,form组件是做是统一管理具体执行还是在form-item中

  • Проверить всю форму

          // 对整个表单进行验证
          validate(callback) {
            // 没有表单数据 抛警告跳出
            if (!this.model) {
              console.warn('[Element Warn][Form]model is required for validate to work!');
              return;
            }
    
            let promise;
            // 没有callback并且浏览器支持Promise return promise
            if (typeof callback !== 'function' && window.Promise) {
              promise = new window.Promise((resolve, reject) => {
                callback = function(valid) {
                  valid ? resolve(valid) : reject(valid);
                };
              });
            }
              
            let valid = true;
            let count = 0;
            // 如果需要验证的fields为空,调用验证时立刻返回callback
            if (this.fields.length === 0 && callback) {
              callback(true);
            }
            let invalidFields = {};
            // 遍历所有实例,一个个验证
            this.fields.forEach(field => {
              // 这里的validate是form-item的方法
              field.validate('', (message, field) => {
                // 如果有返回信息, 则说明验证失败
                if (message) {
                  valid = false;
                }
                // 将错误对象复制到invalidFields
                invalidFields = objectAssign({}, invalidFields, field);
                // 调callback
                if (typeof callback === 'function' && ++count === this.fields.length) {
                  callback(valid, invalidFields);
                }
              });
            });
    
            if (promise) {
              return promise;
            }
          }
    

    Метод objectAssign находится в utils/merge, метод слияния объектов

  • Проверка некоторых форм

          // 对部分表单验证
          validateField(prop, cb) {
            let field = this.fields.filter(field => field.prop === prop)[0];
            if (!field) { throw new Error('must call validateField with valid prop string!'); }
            // 验证对应表单规则的表单
            field.validate('', cb);
          }
    
  • Удалить результат проверки элемента формы

    传入待移除的表单项的 prop 属性组成的数组,如不传则移除整个表单的校验结果

          clearValidate(props = []) {
            const fields = props.length
              ? this.fields.filter(field => props.indexOf(field.prop) > -1)
              : this.fields;
            fields.forEach(field => {
            // form-item实例的方法clearValidate(清除验证状态与提示)
              field.clearValidate();
            });
          }
    
  • Сбросить всю форму, сбросить все значения полей до начальных значений и удалить результаты проверки

          resetFields() {
            // 没有表单数据 return
            if (!this.model) {
              // 环境变量,非生产环境抛警告再return
              process.env.NODE_ENV !== 'production' &&
              console.warn('[Element Warn][Form]model is required for resetFields to work.');
              return;
            }
            this.fields.forEach(field => {
              field.resetField();
            });
          }
    

4. Отслеживайте правила проверки

    // 监听表单验证规则
    watch: {
      rules() {
        // validateOnRuleChange未传入false则立即触发
        if (this.validateOnRuleChange) {
          // 验证
          this.validate(() => {});
        }
      }
    }

form-item

структура

  <div class="el-form-item" :class="[{
      'el-form-item--feedback': elForm && elForm.statusIcon,
      'is-error': validateState === 'error',
      'is-validating': validateState === 'validating',
      'is-success': validateState === 'success',
      'is-required': isRequired || required
    },
    sizeClass ? 'el-form-item--' + sizeClass : ''
  ]">
    <!-- 表单域标签文本 -->
    <label :for="labelFor" class="el-form-item__label" :style="labelStyle" v-if="label || $slots.label">
      <slot name="label">{{label + form.labelSuffix}}</slot>
    </label>
    <div class="el-form-item__content" :style="contentStyle">
      <!-- 插槽接收表单验证的元素,input框单选框多选框之类的 -->
      <slot></slot>
      <!-- 验证不通过时的message -->
      <transition name="el-zoom-in-top">
        <div
          v-if="validateState === 'error' && showMessage && form.showMessage"
          class="el-form-item__error"
          :class="{
            'el-form-item__error--inline': typeof inlineMessage === 'boolean'
              ? inlineMessage
              : (elForm && elForm.inlineMessage || false)
          }"
        >
          {{validateMessage}}
        </div>
      </transition>
    </div>
  </div>

Внешний блок управляет общим стилем, а внутренний разделён на две части.

  • Первая часть: может отображать метку или объединяющий унифицированный суффикс (атрибут from)
  • Вторая часть: слот получает такие компоненты, как ввод, выбор и отображение сообщения при сбое проверки.

这里的变量elForm就是form组件provide的from实例

раздел сценария

1.Inject получает экземпляры форм и вводит экземпляры элементов формы потомкам.

    provide() { // 注入form-item实例
      return {
        elFormItem: this
      };
    },
    // 接收form实例
    inject: ['elForm']

2. После подключения компонента $el к экземпляру

Для инициализации собираются компоненты формы, которые необходимо проверить, и отслеживаются события el.form.blur и el.form.change с правилами проверки, ожидая запуска проверки.

    mounted() {
      // 有需要验证的表单
      if (this.prop) {
        // 向上查找form组件,并发布el.form.addField,暴露form-item实例
        // 即让form组件收集需要验证的form-item实例
        this.dispatch('ElForm', 'el.form.addField', [this]);
        // 需要验证的表单数据
        let initialValue = this.fieldValue;
        // 是数组
        if (Array.isArray(initialValue)) {
          initialValue = [].concat(initialValue);
        }
        // 响应属性变成普通属性
        Object.defineProperty(this, 'initialValue', {
          value: initialValue
        });
        // 该项验证规则
        let rules = this.getRules();

        if (rules.length || this.required !== undefined) {
          // 监听el.form.blur,回调为bluer事件验证
          // 监听el.form.change事件,回调为change事件验证
          this.$on('el.form.blur', this.onFieldBlur);
          this.$on('el.form.change', this.onFieldChange);
        }
      }
    }
  • Используемый здесь метод отправки представлен из миксинов: найти указанный компонент и опубликовать указанное событие.

3. Метод проверки

      validate(trigger, callback = noop) {
        this.validateDisabled = false;
        // 符合规则的trigger
        const rules = this.getFilteredRule(trigger);
        // 没有规则也不是必填 返回true
        if ((!rules || rules.length === 0) && this.required === undefined) {
          // 执行回调
          callback();
          return true;
        }
        // 验证中
        this.validateState = 'validating';

        const descriptor = {};
        // 为了匹配AsyncValidator插件所需要的格式,需要做规则数据做一些操作
        if (rules && rules.length > 0) {
          rules.forEach(rule => {
            delete rule.trigger;
          });
        }
        // AsyncValidator需要的验证规则
        descriptor[this.prop] = rules;
        // 验证规则AsyncValidator实例对象
        const validator = new AsyncValidator(descriptor);
        const model = {};
        // AsyncValidator需要的验证数据
        model[this.prop] = this.fieldValue;
        // 验证
        validator.validate(model, { firstFields: true }, (errors, invalidFields) => {
          // 验证后的状态
          this.validateState = !errors ? 'success' : 'error';
          // 验证提示
          this.validateMessage = errors ? errors[0].message : '';
          // 执行回调
          callback(this.validateMessage, invalidFields);
          // form组件发布validate事件
          this.elForm && this.elForm.$emit('validate', this.prop, !errors);
        });
      }

4. Очистите статус проверки и сбросьте метод проверки формы.

      // 清空验证状态及message
      clearValidate() {
        // 验证状态
        this.validateState = '';
        // 验证message
        this.validateMessage = '';
        this.validateDisabled = false;
      },
      // 重置
      resetField() {
        this.validateState = '';
        this.validateMessage = '';

        // 拿到初始数据
        let model = this.form.model;// 所以表单数据
        let value = this.fieldValue; // 该项表单数据
        let path = this.prop; //该项
        if (path.indexOf(':') !== -1) {
          path = path.replace(/:/, '.');
        }
        // 该项表单数据
        let prop = getPropByPath(model, path, true);

        this.validateDisabled = true;
        // 重置为初始表单数据
        if (Array.isArray(value)) {
          prop.o[prop.k] = [].concat(this.initialValue);
        } else {
          prop.o[prop.k] = this.initialValue;
        }
        // 向下寻找select组件,发布fieldReset事件暴露初始表单数据
        this.broadcast('ElTimeSelect', 'fieldReset', this.initialValue);
      }

5. Отслеживайте ошибки и проверяйте статус

Ошибка здесь принимается реквизитами: если она передается, статус меняется на ошибка, и отображается сообщение об ошибке.

    watch: {
      // 监听error
      error: {
        // 立即执行handler
        immediate: true, 
        handler(value) {
          // 验证状态变为error,并显示错误信息
          this.validateMessage = value;
          this.validateState = value ? 'error' : '';
        }
      },
      // 监听验证状态
      validateStatus(value) {
        this.validateState = value;
      }
    }
6. Перед уничтожением экземпляра опубликуйте форму, чтобы удалить собранный экземпляр элемента формы.
    beforeDestroy() {
      // 向上寻找form组件,发布el.form.removeField事件,暴露当前实例
      this.dispatch('ElForm', 'el.form.removeField', [this]);
    }

Связь между компонентами формы и такими компонентами, как ввод

是怎么触发from验证的呢

Форма прослушивает события el.form.blur и el.form.change при монтировании и указывает функцию обратного вызова проверки.

this.$on('el.form.blur', this.onFieldBlur);
this.$on('el.form.change', this.onFieldChange);

Взять ввод в качестве примера

// form表单发布el.form.blur事件
this.dispatch('ElFormItem', 'el.form.blur', [this.currentValue]);

В качестве примера возьмем множественный выбор флажка

// 监听value 向上寻找form组件发布el.form.change事件暴露value(数组)
this.dispatch('ElFormItem', 'el.form.change', [value]);

подождите... , эти тайминги запускают проверку

$onЭто прослушивание указанного события и указание функции обратного вызова,$emitОпубликовать событие и передать некоторые данные (или нет), когда имя отслеживаемого события совпадает с именем опубликованного события, будет запущена отслеживаемая функция обратного вызова, а параметр будет соответствующим$emitпереданные параметры.

Метод прямого экземпляра официального сайта$onтак же как$emit