elementUI form表单的实现

1,278 阅读1分钟

1楔子

最近组里在推进和沉淀一些通用的VUE UI组件,所以学习和参考了elementUI的实现方式。form表单其实是很常用的一种基础组件。今天就来研究下里面的实现

2 需要用到的知识点

1. provide inject

它是一种祖先向后代传递数据的一种方式。说到组件与组件之间的通信,有传统的props属性传递,复杂一点用ebus,大型复杂项目推进vuex。

initInjects(vm) // 在初始化data之前,也就是在data中是可以使用祖先注入的变量的
...
initState(vm);
initProvide(vm); // resolve provide after data/props 
callHook(vm, 'created');

2. $on $emit

vue的通过自定义事件,进行子到父的消息传递。

3. inheritAttrs

设置是否默认继承父组件的属性。一般设置为false,然后通过v-bind="$attrs",绑定到需要的地方。如果不设置的话,会默认在组件的最外层dom设置。

4 slot相关

默认插槽 具名插槽 作用域插槽,具体参考文档

5 async-validator

一个异步校验库,这里可以将这块的实现和form本身的代码拆开来,难度就会减少很多

3 demo

// MfInput
<template>
  <div class="mf-input">
    <input :value="value" @input="onInput" v-bind="$attrs">
  </div>
</template>

<script>
  export default {
    inheritAttrs: false,
    name: 'MfInput',
    props: {
      value: {
        type: String,
        default: ''
      },
    },
    methods: {
      onInput(e) {
        this.$emit('input', e.target.value)
        this.$parent.$emit('validate')
      }
    },
  }
</script>
// MfFormitem
<template>
  <div class="mf-formitem">
    <label v-if="label">{{label}}</label>
    <slot></slot>
    <div v-if="errorMsg">{{errorMsg}}</div>
  </div>

</template>

<script>
  import schema from 'async-validator'
  export default {
    name: 'MfFormItem',
    inject: ['form'],
    props: {
      label: {
        type: String,
        default: ''
      },
      prop: {
        type: String,
        default: ''
      }
    },
    data() {
      return {
        errorMsg: ''
      }
    },
    mounted(){
      this.$on('validate', () => this.validate())
    },
    methods: {
      validate() {
        const value = this.form.model[this.prop]
        const rule = this.form.rules[this.prop]
        console.log(value, rule)

        let validator = new schema({[this.prop]: rule})
        return validator.validate({[this.prop]: value}, (errors, fields) => {
          if( errors ){
            this.errorMsg = errors[0].message
          } else {
            this.errorMsg = ''
          }
        })
      }
    },
  }
</script>
// MfForm
<template>
  <div class="mf-form">
    <slot></slot>
  </div>
</template>

<script>
  export default {
    name: 'MfForm',
    props: {
      model: {
        type: Object,
        required: true,
        default: () => {}
      },
      rules: {
        type: Object,
        default: () => {}
      },

    },
    provide(){
      return {
        form: this
      }
    },
    methods: {
      validate(cb) {
        let tasks = this.$children.filter((item) => item.prop).map((item) => item.validate())
        Promise.all(tasks).then(function() {
          cb(true)
        }).catch(function() {
          cb(false)
        })
      }
    },
  }
</script>

4 后记

将第二部分的知识点理解之后再去看element的form实现,就会感觉很通畅了。