剖析ant-design-vue中Form原理

1,023 阅读1分钟

我们带着下面的疑问来剖析form的原理:

  1. Form表单上的fields存在哪里
  2. Form.Item如何与field做关联
  3. 表单项改变,Form为什么会重新渲染

ant-design-vue中的Form

这里主要说template的写法的情况下,如果是jsx并不一样

this.$form.createForm 函数 生成的是什么?
export default {
  // ...
  data() {
    return {
      form:this.$form.createForm(this, {
        name: 'demo-form-one'
      })
    }
  }
  // ...
}
  1. this.$form.createForm方法 在components/form/Form.jsx中,通过代码可知,这个方法返回的是一个Vue实例
// ...
  createForm(context, options = {}) {
    const V = Base.Vue || Vue;
    return new V(Form.create({ ...options, templateContext: context })());
  },
// ...
  1. Form.create经过层层的引用,最终调用的是createBaseForm 下 decorate 函数的方法 decorate 将返回生成Vue实例所需要的配置项
function createBaseForm(option = {}, mixins = []) {
  const {
    // ...
    templateContext
  } = option;
  // ...
  return function decorate(WrappedComponent) { // 最终调用的就是这个方法
    const Form = { /* ... */ }
    // ...
    if (!WrappedComponent) return Form;
  }
  // ...
}
  1. 在Vue的配置对象Form中,通过createFieldsStore生成fieldsStore,同时将fieldsStore映射到this上
const Form = {
  data() {
    // ...
    this.fieldsStore = createFieldsStore(fields || {});
    this.templateContext = templateContext;

    [
      'getFieldsValue',
      'getFieldValue',
      'setFieldsInitialValue',
      'getFieldsError',
      'getFieldError',
      'isFieldValidating',
      'isFieldsValidating',
      'isFieldsTouched',
      'isFieldTouched',
    ].forEach(key => {
      this[key] = (...args) => {
        return this.fieldsStore[key](...args);
      };
    });
    // ...
  }
}
表单组件的数据是如何绑定上去的
<a-input
  ref="input"
  v-decorator="['note', {
    rules: [
      { required: true },
      { message : 'Please input your note!'}
    ]
  }]"
/>
  1. FormItem.jsx 中 对 子组件数组 调用了 decoratorChildren 方法
export default {
  name: 'AFormItem',
  render() {
    let child = filterEmpty($slots.default || []);
    // ...
    child = cloneVNodes(child);
    this.slotDefault = this.decoratorChildren(child);
    // ...
    return this.renderFormItem();
  }
}
  1. decoratorChildren 方法中通过decoratorOption获得指令的参数,然后通过getFieldDecorator对组件进行再次封装
decoratorChildren(vnodes) {
  const { FormContext } = this;
  const getFieldDecorator = FormContext.form.getFieldDecorator;
  for (let i = 0, len = vnodes.length; i < len; i++) {
    // ...
    const option = this.decoratorOption(vnode);
    if (option && option[0]) {
      vnodes[i] = getFieldDecorator(option[0], option[1], this)(vnode);
    }
    // ...
  }
  return vnodes;
}

decoratorOption(vnode){
   if (vnode.data && vnode.data.directives) {
        const directive = find(vnode.data.directives, ['name', 'decorator']);
        // ...
        return directive ? directive.value : null;
   }
   // ...
}
  1. getFieldDecorator 方式是 定义在 createBaseForm里面, 通过getFieldProps 获得field相关的属性,最后通过cloneElement绑定到fieldElem上
getFieldDecorator(name, fieldOption, formItem) {
  // 主要步骤,下面的可以忽略只是生成新Elm
  const { props, ...restProps } = this.getFieldProps(name, fieldOption);
  // ...
  return (fieldElem)=> {
    // ...
    const newProps = {
      props: {
        ...props,
        ...this.fieldsStore.getFieldValuePropValue(fieldMeta),
      },
      ...restProps,
    };
    newProps.domProps.value = newProps.props.value;
    const newEvents = {};
    Object.keys(newProps.on).forEach(key => {
      if (originalEvents[key]) {
        const triggerEvents = newProps.on[key];
        newEvents[key] = (...args) => {
          originalEvents[key](...args);
          triggerEvents(...args);
        };
      }
      // ...
    });
    return cloneElement(fieldElem, { ...newProps, on: newEvents });
  }
  // ...
}
  1. getFieldProps方法做了很多操作,其中有如下两个
  2. 取出fieldsStore中相关的值,作为value
  3. 绑定onCollectValidate事件到change上
getFieldProps(name) {
  // ...
  // 这边获取value
  const inputProps = {
    ...this.fieldsStore.getFieldValuePropValue(fieldOption)
  };
  // 这边绑定change事件,每次表单组件更新时,fieldStore里面相关的信息也会更新
  const validateTriggers = getValidateTriggers(validateRules);
  validateTriggers.forEach(action => {
    if (inputListeners[action]) return;
    inputListeners[action] = this.getCacheBind(name, action, this.onCollectValidate);
  });
  // ...
  return {
    // ...
    domProps: {
      value: inputProps.value,
    },
    on: inputListeners
  }
}

onCollectValidate(name_, action, ...args){
  // 获取field信息
  const { field, fieldMeta } = this.onCollectCommon(name_, action, args);
  // ...
  this.validateFieldsInternal([newField], {
    action,
    options: {
      firstFields: !!fieldMeta.validateFirst,
    },
  });
}
  1. 在validateFieldsInternal中进行校验,检验通过后同步到store中, 并使form重新渲染
validateFieldsInternal(){
  // ...
  this.setFields(nowAllFields);
  //...
}

setFields(){
  this.fieldsStore.setFields(fields);
  //...
  const formContext = templateContext || this;
  let allUpdate = false;
  Object.keys(changedFields).forEach(key => {
    //...
    if (formItem && formItem.itemSelfUpdate) {
      formItem.$forceUpdate();
    } else {
      allUpdate = true;
    }
  });
  if (allUpdate) {
    formContext.$forceUpdate();
  }
  //...
}