vue组件化开发实战 - 实现简易ElementUI的Form表单组件

Posted juyouji

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了vue组件化开发实战 - 实现简易ElementUI的Form表单组件相关的知识,希望对你有一定的参考价值。

Input 组件

功能:

  • 进行数据双绑
  • 通知FormItem组件校验
<template>
  <div>
     <input  :type="type" :value="value" @input="onInput" v-bind="$attrs" >
  </div>
</template>
<script>
export default {
    inheritAttrs:false,
    props:{
        value:{
            type: String,
            default: ''
        },
        type:{
            type: String,
            default: 'text'
        },  
    },
    methods:{
        onInput(e){
            this.$emit('input',e.target.value);
            // 让父组件自己派发自己监听该valid事件进行校验。 
            this.$parent.$emit('valid');
        }
    }
}
</script>

$attrsv-bind="$attrs" 使用

  • $attrs是vue内置属性用来存储外部传向一个组件但没有被props接收的属性。
  • 这些属性都有非prop特性
    1. 会被自动添加到组件的根元素上。
    2. 默认情况下,非prop特性的属性会覆盖组件根元素上同名的内容。 对于styleclass有特殊处理:进行合并(但是如果是同名样式则会覆盖)
    3. 如果不希望组件根元素继承非prop特性,可以在组件配置项中配置 inheritAttrs:false ( 该API对style和class没影响 )

v-bind="$attrs"是对$attrs进行解构赋值的一种方法,解构成:key="value"形式。

FormItem 组件

功能:

  • 校验
  • label显示
  • 错误显示
<template>
  <div>
    <label v-if="label">{{label}}</label>
    <slot></slot>      
    <p v-if="error">{{error}}</p>
  </div>
</template>
<script>
  import Schema from 'async-validator'; // 校验包
  export default {
    inject:['form'],
    props:{
      label:{
        type:String,
        default:''
      },
      prop:{
        type:String,
        default:''
      }
    },
    data(){
      return {
        error:''
      }
    },
    mounted(){
      this.$on('valid',()=>{
          this.validate();
      })
    },
    methods:{
      validate(){
        const rules = this.form.rules[this.prop];
        const value = this.form.model[this.prop];

        const schema = new Schema({[this.prop]:rules});
        // 返回Promise<Boolean>
        return schema.validate({[this.prop]:value},(errors)=>{
          if(errors){
            this.error = errors[0].message;
          }else{
            this.error = '';
          }
        })
      }
    }
  }
</script>   

Form 组件

功能:

  • 提供数值
<template>
    <div>
        <slot></slot>
    </div>
</template>
<script>
export default {
    provide(){
        return {
            form:this  // 把form组件实例传给后代,后代可以通过该实例访问该组件上的属性 model、rule ...
        }
    },
    props:{
        model:{
            type:Object,
            reuqired:true
        },
        rules:{
            type:Object,
        }
    },
    methods:{
        validate(cb){
            const checkRes = this.$children
            .filter(item => item.prop)
            .map(item => item.validate());

            Promise.all(checkRes)
            .then(()=>cb(true))
            .catch(()=>cb(false));
        }
    }
}
</script>

通用库开发中常用的跨层级传参方法 provide / inject

  1. 祖代设置provide配置项,该项类似于data的用法,但是它的数据是给后代用的,不是给自己用的。
  2. 后代通过inject配置项获取祖代提供的数据。

        inject: ['form'] // 祖代provide中的key值

    通过该方式可以避免第三方库的引入如vuex,如果层级深也不需要通过props的方法进行繁琐的传递。

使用

<template>
    <div>
        <Form :model="model" :rules="rules" ref="myform">
            <FormItem label="用户名" prop="username">
                <Input v-model="model.username" placeholder="输入用户名" @ev="func" />
            </FormItem>
            <FormItem label="密码" prop="password" >
                <Input v-model="model.password" placeholder="输入密码" type="password" @ev="func" />
            </FormItem>
             <FormItem >
                 <button @click="submitForm('myform')">提交</button>
            </FormItem>
        </Form>
    </div>
</template>
<script>
import Input from '@/components/form/Input'; 
import Form from '@/components/form/Form'; 
import FormItem from '@/components/form/FormItem';
export default {
    components:{
        Input,
        Form,
        FormItem
    },
    data(){
        return{
            model:{
                username:'',
                password:'',
            },
            rules:{
                username:[{required:true,message:'用户名必填'}],            
                password:[{required:true,message:'密码必填'}],
            },
        }
    },
    methods:{
        submitForm(form){
            this.$refs[form].validate(valid=>{
                if(valid){
                    alert('校验通过');
                }else{
                    alert('校验失败');
                }
            })
        }
    }
}
</script> 

问题总结

  1. 为什么数据模型model放在form上?
    为了验证方便,From内的FormItem验证可能有很多,如果都写在FormItem上会很繁琐。
  2. FormItem上为什么要写prop?
    为了判别是对model里的哪部分进行校验,也是通过这个prop去获取规则中的key。

以上是关于vue组件化开发实战 - 实现简易ElementUI的Form表单组件的主要内容,如果未能解决你的问题,请参考以下文章

vue.js实战:如何构建你的第一个Vue.js组件

Vue 开发实战实战篇 # 33:更加精细化的权限设计(权限组件权限指令)

vue实现一个简易Popover组件

Vue 开发实战实战篇 # 45:如何构建可交互的组件文档让代码高亮的显示在页面

Vue 开发实战基础篇 # 4:Vue组件的核心概念:插槽

前端开发Vue + Fabric.js + Element-plus 实现简易的H5可视化图片编辑器