vue基础之自定义组件

237 阅读2分钟

刚开始学vue的一篇笔记,闲来无事翻出来瞅瞅,顺便整理下,希望可以帮助刚入门vue的同学吧。

先看一个成熟的表单组件的基本功能 html结构:

<el-form :model="model" :rules="rules">
	<el-form-item label="用户名" prop="username">
		<el-input v-model="model.username" autocomplete="off"></el-input>
	</el-form-item>
	<el-form-item label="密码" prop="password">
		<el-input type="password" v-model="model.password" autocomplete="off"></el-input>
	</el-form-item>
	<el-form-item>
		<el-button type="primary" @click="submitForm('loginForm')">提交</el-button>
	</el-form-item>
</el-form>

vue代码: 在vue的data中定义数据模型和校验规则

  data() {
    return {
      model: { username: "", password: "" },
      rules: {
        username: [
          { required: true, message: "请输入用户名" }
        ],
        password: [
          { required: true, message: "请输入密码" },
           {min: 6,max:12,message:'请输入6~12的密码'}
        ],
      }
    };
  },

这是elementUI的表单组件,废话也不多写了,下面就仿造这个使用方式去实现一个我们自己的表单组件。 观察上面的html结构,分为最外层的 form,以及内部的 form-iteminputform主要是用来接收绑定数据模型以及校验规则的。 form-item是用来做校验的,并显示错误提示。 input就是一个普通的标签,与数据模型双向绑定。

废话不多写了,直接放上代码 App.vue代码:

<template>
  <div id="app">
    <template>
      <!--仿造elementUI的使用方式-->
      <my-form :model="model" :rules="rules" >
        <my-form-item label="用户名" prop="username">
          <my-input v-model="model.username"></my-input>
        </my-form-item>
        <my-form-item label="密码" prop="password">
          <my-input v-model="model.password" type="password"></my-input>
        </my-form-item>
      </my-form>
    </template>
  </div>
</template>

<script>
import MyInput from './components/Input.vue';
import MyFormItem from './components/FormItem.vue';
import MyForm from './components/Form.vue';
export default {
  name: "app",
  components: {
    MyInput,
    MyFormItem,
    MyForm
  },
  data() {
    return {
      // 数据模型
      model: { username: "", password: "" },
      // 校验规则
      rules: {
        username: [
           {required: true, message: "请输入用户名" }
        ],
        password: [
           {required: true, message: "请输入密码" },
           {min: 6,max:12,message:'请输入6~12的密码'}
        ],
      }
    };
  }
};
</script>

自定义组件的使用方式是仿造elementUI的。所以内部也按照这种方式去实现。 首先要实现的是自定义input。

Input.vue文件的代码如下:

<template>
    <div>
        <input :type="type" :value="value" @input="onInput">
    </div>
</template>

<script>
export default {
  props: {
    value: {
      type: String,
      default: ""
    },
    type: {
      type: String,
      default: "text"
    }
  },
  methods: {
    onInput(e) {
      let value = e.target.value;
      this.$emit("input", value);
      this.$parent.$emit("validate");
    }
  }
};
</script>

<style scoped>
</style>

可以看到自定义input组件内部就是一个普通的input,我们要做的就是为这个input实现双向绑定。

  • props中的value是在父组件中v-model双向绑定传进来的值。
  • props中的type是绑定的输入框的类型,text、password。并且将值绑定到input标签上。
  • input标签上的:value绑定的就是props传进来的value值。也就是说如果在自定义input数据模型发生改变,也能及时的更新input输入框中的值。
  • input标签上的input的事件,如果input标签内容发生改变就去执行onInput方法,onInput函数再去触发input事件,使数据模型也跟着改变,这就实现了双向绑定。

onInput方法就是在input标签的值发生改变时触发,然后获取到改变后的值,触发自定义组件的input事件,并将新值作为参数传递,通知数据模型更新。另外就是触发父组件中的validate方法,通知上一级组件form-item,“我的值改变了,请重新校验”。

FormItem.vue文件代码如下:

<template>
    <div>
        <label>{{label}}</label>
        <div>
            <slot></slot>
            <p v-if="errStatus">{{errMessage}}</p>
        </div>
    </div>
</template>

<script>
import Schema from "async-validator";
export default {
  inject: ["myForm"],
  props: ["label", "prop"],
  data() {
    return {
      errMessage: "",
      errStatus: false
    };
  },
  mounted() {
    // 监听下级input组件中触发的事件
    this.$on("validate", this.validator);
  },
  methods: {
    // 校验方法
    validator() {
      const rules = this.myForm.rules[this.prop];
      const value = this.myForm.model[this.prop];

      // 描述对象
      const descriptor = { [this.prop]: rules };
      const schema = new Schema(descriptor);
      schema.validate({ [this.prop]: value }, errors => {
        if (errors) {
          this.errMessage = errors[0].message;
          this.errStatus = true;
        } else {
          this.errMessage = "";
          this.errStatus = "";
        }
      });
    }
  }
};
</script>

首先html结构中预留出放input组件的插槽。 在props中获取到传进来的标题(label)、校验字段。 form-item组件的核心就是validator方法,首先方法内部需要拿到form组件上的校验规则和数据模型,这里通过provide inject选项,这是vue2.2新增的内容。(这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效(不做展开说明,自行到官方文档查阅))。这里我们inject进来的myForm,就是在form组件里provide的一个变量,如果将form组件的实例注入进来,是不是就可以拿到校验规则和数据模型了呢(二者都已经在form组件实例中了)。通过this.prop当前需要校验的数据模型中的字段名,就可以拿到对应的值和校验规则表中的规则。 校验部分引入第三方库进行校验,此时需要做的就是构造一个供初始化Schema使用的对象,格式就是{字段名:规则},如{ [this.prop]: rules };,用中括号将this.prop扩起来就是要将this.prop的值作为对象的key。 接下来就是调用validate方法做校验了。

Form.vue文件代码如下:

<template>
    <form>
        <slot></slot>
    </form>
</template>

<script>
    export default {
        provide(){
            return {
                myForm: this
            }
        },
        props:{
            model:{
                type:Object,
                required:true
            },
            rules:{
                type:Object
            }
        }
    }
</script>

html结构中预留出form-item组件的插槽,props接收数据模型和校验规则,然后使用provide选项将自身实例传入,供子集使用访问数据模型和校验规则。

到这一个简易的自定义表单组件就完成了。