element框架form组件源码分析以及dispatch原理

1. form和form-item组件

先给出一个官网使用代码

<template>
<el-form :model="ruleForm" status-icon :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">
  <el-form-item label="密码" prop="pass">
    <el-input type="password" v-model="ruleForm.pass" autocomplete="off"></el-input>
  </el-form-item>
  <el-form-item label="确认密码" prop="checkPass">
    <el-input type="password" v-model="ruleForm.checkPass" autocomplete="off"></el-input>
  </el-form-item>
  <el-form-item label="年龄" prop="age">
    <el-input v-model.number="ruleForm.age"></el-input>
  </el-form-item>
  <el-form-item>
    <el-button type="primary" @click="submitForm('ruleForm')">提交</el-button>
    <el-button @click="resetForm('ruleForm')">重置</el-button>
  </el-form-item>
</el-form>
</template>
<script>
  export default {
    data() {
      var checkAge = (rule, value, callback) => {
        if (!value) {
          return callback(new Error('年龄不能为空'));
        }
        setTimeout(() => {
          if (!Number.isInteger(value)) {
            callback(new Error('请输入数字值'));
          } else {
            if (value < 18) {
              callback(new Error('必须年满18岁'));
            } else {
              callback();
            }
          }
        }, 1000);
      };
      var validatePass = (rule, value, callback) => {
        if (value === '') {
          callback(new Error('请输入密码'));
        } else {
          if (this.ruleForm.checkPass !== '') {
            this.$refs.ruleForm.validateField('checkPass');
          }
          callback();
        }
      };
      var validatePass2 = (rule, value, callback) => {
        if (value === '') {
          callback(new Error('请再次输入密码'));
        } else if (value !== this.ruleForm.pass) {
          callback(new Error('两次输入密码不一致!'));
        } else {
          callback();
        }
      };
      return {
        ruleForm: {
          pass: '',
          checkPass: '',
          age: ''
        },
        rules: {
          pass: [
            { validator: validatePass, trigger: 'blur' }
          ],
          checkPass: [
            { validator: validatePass2, trigger: 'blur' }
          ],
          age: [
            { validator: checkAge, trigger: 'blur' }
          ]
        }
      };
    },
    methods: {
      submitForm(formName) {
        this.$refs[formName].validate((valid) => {
          if (valid) {
            alert('submit!');
          } else {
            console.log('error submit!!');
            return false;
          }
        });
      },
      resetForm(formName) {
        this.$refs[formName].resetFields();
      }
    }
  }
</script>
包含
包含
el-form
el-form-item
el-input或el-button

结构是这样的,在加载顺序上vue是:

  • 父组件先执行created,但是没有挂载
  • 接着子组件执行created,然后mounted。
  • 父组件接着mounted
1.父组件created
2. 子组件created
3. 子组件 mounted
父组件mounted
1. form组件在created时做了什么?

做了两件事:

created() {
    this.$on('el.form.addField', (field) => {
            if (field) {
            this.fields.push(field);
        }
    });
   	/* istanbul ignore next */
    this.$on('el.form.removeField', (field) => {
        if (field.prop) { // 表单form-item的属性
        this.fields.splice(this.fields.indexOf(field), 1);
        }
    });
}

组件 o n 监 听 当 前 实 例 上 的 自 定 义 事 件 。 事 件 可 以 由 ‘ v m . on监听当前实例上的自定义事件。事件可以由 `vm. onvm.emit` 触发。回调函数会接收所有传入事件触发函数的额外参数。

所以两件事:

  • 监听el.form.addField自定义事件,如果这个事件被触发,就将field(子组件form-item实例对象)添加到 组件data的fields数组中

  • 监听el.form.removeField自定义事件,如果事件被触发,就执行函数,函数中判断如果form-item实例对象还有prop属性。form-item的props中prop,这个属性在element官网中的可以查询到。prop: 表单域 model 字段,在使用 validate、resetFields 方法的情况下,该属性是必填的。如果在form-item组件销毁的时候会触发(比如v-if为false)

    //form-item组件中的beforeDestory中做的事情。
    beforeDestroy() {
        this.dispatch('ElForm', 'el.form.removeField', [this]);
    }
    

    这里有个dispatch,是element的自己实现的,据说的是vue1.x中dispatch的阉割版。但是我也研究过vue源码

    具体dispatch怎么实现的,请看本文中的element的dispatch实现原理。

2. form-item组件在mounted组件中做了什么?

form-item在created没有操作,但是在mounted做了些操作

mounted() {
    if (this.prop) {
    	this.dispatch('ElForm', 'el.form.addField', [this]);

    	let initialValue = this.fieldValue;
        if (Array.isArray(initialValue)) {
            initialValue = [].concat(initialValue);
        }
        Object.defineProperty(this, 'initialValue', {
            value: initialValue
        });

        this.addValidateEvents();
    }
}
  • 如果这个组件有props中的prop属性有值,那么dispatch到父组件触发el.form.addField事件。这个addField,上面有提到过,是将子组件实例对象form-item添加到form组件实例对象的fields数组中。

——————待补全------------------------------------------------------------

2. element中emitter.js
1. dispatch

使用过vuex的肯定知道vuex中也有dispatch, vuex中的disaptch是对action的派发,进入到mutation的变更。

dispatch(componentName, eventName, params) {
    var parent = this.$parent || this.$root; //找到组件的父组件,或者这个组件本就是根组件。
    var name = parent.$options.componentName;//获取到组件的componentName的值

    while (parent && (!name || name !== componentName)) {
    //只要parent为true,并且有name为false,或者name和componentName不全等,就一直循环。
        parent = parent.$parent; //获取上一层组件实例对象

        if (parent) {//如果上层组件存在,重新将上层组件的componentName的值赋值给他
                name = parent.$options.componentName;
        }
    }
    if (parent) {
    	parent.$emit.apply(parent, [eventName].concat(params));
    }
}

单从dispatch的函数参数名中就可以看出是是对事件的派发,函数中使用vm.$emit触发事件。其中的componentName是element组件自定义的一个属性.

//element的form组件
 export default {
    name: 'ElForm',
    componentName: 'ElForm'
    //...
  }

我们先看一个调用dispatch方法的例子

//form-item中mounted执行了这句话。this是form-item实例对象
this.dispatch('ElForm', 'el.form.addField', [this]);

那么这个dispatch一共做了两件事:

  • 找到这个组件指定的父组件
  • 使用vm.$emit触发这个父组件的事件(包括自定义事件)

还有就是那个while循环干了啥,为啥要循环:

  • 原因就在于你要找的父组件不是直接父组件,如:
<el-form >
  <el-row>
    <el-col :span="24">
      <el-form-item prop="checkPass">
        <el-tag>标签一</el-tag>
      </el-form-item>
    </el-col>
  </el-row>
</el-form>

如上,我el-form-item组件要触发el-form中的自定义事件,但是el-form不是直接父组件。所有while循环做了一件事情,就是层层往上找,直到找到这个父组件或者直到根组件也没有找到想要的,那么这个parent就是undefined。undefined也就不会执行下面的$emit了。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值