我们带着下面的疑问来剖析form的原理:
- Form表单上的fields存在哪里
- Form.Item如何与field做关联
- 表单项改变,Form为什么会重新渲染
ant-design-vue中的Form
这里主要说template的写法的情况下,如果是jsx并不一样
this.$form.createForm 函数 生成的是什么?
export default {
// ...
data() {
return {
form:this.$form.createForm(this, {
name: 'demo-form-one'
})
}
}
// ...
}
- this.$form.createForm方法 在
components/form/Form.jsx
中,通过代码可知,这个方法返回的是一个Vue实例
// ...
createForm(context, options = {}) {
const V = Base.Vue || Vue;
return new V(Form.create({ ...options, templateContext: context })());
},
// ...
- Form.create经过层层的引用,最终调用的是createBaseForm 下 decorate 函数的方法 decorate 将返回生成Vue实例所需要的配置项
function createBaseForm(option = {}, mixins = []) {
const {
// ...
templateContext
} = option;
// ...
return function decorate(WrappedComponent) { // 最终调用的就是这个方法
const Form = { /* ... */ }
// ...
if (!WrappedComponent) return Form;
}
// ...
}
- 在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!'}
]
}]"
/>
- FormItem.jsx 中 对 子组件数组 调用了 decoratorChildren 方法
export default {
name: 'AFormItem',
render() {
let child = filterEmpty($slots.default || []);
// ...
child = cloneVNodes(child);
this.slotDefault = this.decoratorChildren(child);
// ...
return this.renderFormItem();
}
}
- 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;
}
// ...
}
- 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 });
}
// ...
}
- getFieldProps方法做了很多操作,其中有如下两个
- 取出fieldsStore中相关的值,作为value
- 绑定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,
},
});
}
- 在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();
}
//...
}