vue——如何编写自定义组件

刚开始学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-iteminput
form主要是用来接收绑定数据模型以及校验规则的。
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选项将自身实例传入,供子集使用访问数据模型和校验规则。

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容