Vue组件化实战

介绍

本文目的是对Vue组件化内容在网课学习的总结和复习。多有不足之处,请多多交流。

承接上文(Vue组件化总结),学习了Vue组件化原理,自然是要来一波实战练习的。在网课上面,老师以element-ui的form表单为蓝本,从0 到1的讲述了如何自行设计一个表单组件。其内容包含输入框组件、表单项组件、表单组件以及简单的表单数据校验功能。

组件基础实现

输入框组件

el-input分析

先看下el-input组件的使用方式,再进行分析

<el-input v-model="input" placeholder="请输入内容"></el-input>

以上代码是el-input组件的简单使用形式,由此可以分析得出:

  • el-input组件内部有原始的input输入框
  • el-input组件拥有双向绑定值input
  • el-input组件上的placeholder等属性是可以传递给其内部的input输入框使用的
组件使用v-model

PS:这里是对以上分析的第二点进行补充,在组件使用v-model双向绑定,其内部原理是什么?

Vue的官方文档中是这样解释的:组件上的 v-model 默认会利用名为 value 的 prop属性 和名为 input 的事件(仅指输入框组件,下拉框等另有说明)。

所以,上文的写法相当于在el-input组件中的input上绑定了value和input事件。

mine-input实现

根据上述分析,我们实现出来的代码:

<template>
  <div>
    <input :value="value" @input="onInput" v-bind="$attrs" />
  </div>
</template>
<script>
export default {
  inheritAtrrs: false,
  props: {
    value: {
      type: String,
      default: "",
    },
  },
  methods: {
    onInput(e) {
      this.$emit("input", e.target.value);
    },
  },
};
</script>

在我们实现的组件中,

  • 通过props属性传递进来了v-model绑定的value属性,并声明了input事件的触发方法onInput。

    • 这部分实现在官方文档中有
  • $attrs和inheritAttrs的配合使用是为了接收type、placeholder等属性。

    • 在组件使用处传递的数据,如果没有在内部的props属性接收范围内,都在$attrs里面。
    • 设置inheritAttrs:false的原因是,防止父组件传递的数据挂载到子组件的根元素上
mine-input使用
<mine-input v-model="model.username"></mine-input>
<mine-input type="password" v-model="model.password"></mine-input>

这样的使用效果就和el-input的效果差不多了。

表单项组件

el-form-item分析

先看下el-form-item组件的使用方式,再做分析

<el-form-item label="活动名称" prop="name">
  xxx
</el-form-item>

可以知道的是:

  • el-form-item组件内部接收label、prop等参数
  • el-form-item组件内部有标签可以显示label内容和错误提示内容
  • el-form-item组件内部有匿名插槽,可以显示el-input等组件内容
mine-form-item实现

根据上述分析,我们实现出来的代码:

<template>
  <div>
    <label v-if="label">{{ label }}</label>
    <slot></slot>
    <p v-if="error">{{ error }}</p>
  </div>
</template>
<script>
export default {
  props: {
    label: {
      type: String,
      default: "",
    },
    prop: {
      type: String,
      default: "",
    },
  },
  data() {
    return {
      error: "",
    };
  },
};
</script>

在我们实现的组件中

  • props属性中接收了传递进来的label和prop属性。
  • label标签用于显示label参数的值;p标签用于显示错误提示
  • slot插槽用于显示min-form-item中的内容
mine-form-item使用
<mine-form-item label="用户名" prop="username">
  <mine-input v-model="model.username"></mine-input>
</mine-form-item>
<mine-form-item label="密码" prop="password">
  <mine-input type="password" v-model="model.password"></mine-input>
</mine-form-item>

这样的使用效果就和el-form-item的效果差不多了。

表单组件

el-form分析

先看下el-form组件的使用方式,再做分析

<el-form :model="ruleForm" :rules="rules" ref="ruleForm">
  xxx
</el-form>

可以知道的是:

  • el-form组件内部可以接收model和rules属性值

  • el-form组件内部有匿名插槽,用于显示内容元素

  • ref标记用于在表单提交方法里获取表单组件实例

    • ref作用于组件上,则引用指向组件实例
    • ref作用于dom元素上,则引用指向dom元素
mine-form实现

根据上述分析,我们实现出来的代码:

<template>
  <div>
    <form>
      <slot></slot>
    </form>
  </div>
</template>
<script>
export default {
  props: {
    model: { type: Object, required: true },
    rules: { type: Object },
  },
};
</script>

在我们实现的组件中

  • props里接收了model和rules
  • 表单的form标签内部使用slot标签显示组件内容
mine-form使用
<mine-form :model="model" :rules="rules" ref="loginForm">
  <mine-form-item label="用户名" prop="username">
    <mine-input v-model="model.username"></mine-input>
  </mine-form-item>
  <mine-form-item label="密码" prop="password">
    <mine-input type="password" v-model="model.password"></mine-input>
  </mine-form-item>
  <mine-form-item>
    <button @click="submitForm">提交</button>
  </mine-form-item>
</mine-form>

这样的使用效果就和el-form的效果差不多了。

表单校验

经过以上的分析和实现,我们把表单组件的基础架构搭建了起来,但是表单最重要的一个功能还没实现,那就是表单校验功能。

老规矩,先分析一下整体的思路:

  • form组件接收model和rules,但是内容全在匿名插槽里,所以表单项的具体校验不会放form组件里执行,而是在form-item组件中执行。而表单组件里只对form-item的校验结果进行汇总,并给出最终的校验结果。

    • form组件需要通过某种方式将model和rules的内容传递给form-item组件,以便于form-item组件通过prop属性查找当前表单项的值和校验规则
    • 通过provide/inject方式可以将自身this传递到子代组件中
  • form-item组件内可以找到当前项的具体数据和规则,那么数据校验应该是在这一级的组件里进行

    • form-item接收到form传递过来的this,可以通过this.form.model和this.form.rules以及自身的prop属性可以找到当前项的rule和value;
    • 再通过async-validator插件校验方法,并返回校验结果true、false
  • input组件需要在合适的地方以合适的方式触发校验方法

    • 通过parent/child的方式调用父组件(form-item组件)的方法。

mine-input触发校验

由于我们实现的效果很简单,所以可以在mine-input组件的input事件中触发校验

methods: {
  onInput(e) {
    this.$emit("input", e.target.value);
    this.$parent.$emit("validate");
  },
},

$emit的使用方式

第一种

子组件通过this.$emit代用父组件方法,父组件在调用子组件时做监听

// 子组件
<input v-model="value" @input="input"/>
export default {
  methods:{
    input(e){
      this.$emit('change',e.target.value)
    }
  }
}
// 父组件
<cart @change="handleChange"></cart>
export default {
  methods:{
    handleChange(data){
      console.log(data)
    }
  }
}

这也是我们平时最常用的方式,但是在这里明显的不适用。

第二种

子组件通过this.parent.parent.parent.emit调用父组件方法,父组件在创建时通过this.$on做监听

// 子组件
<input v-model="value" @input="input"/>
export default {
  methods:{
    input(e){
      this.$parent.$emit('change',e.target.value)
    }
  }
}
// 父组件
<cart></cart>
export default {
  mounted(){
    this.$on("change",data=>{
      this.handleChange(data)
    })
  },
  methods:{
    handleChange(data){
      console.log(data)
    }
  }
}

这就是我们在实现的代码里使用的方式

第三种

父组件通过props形式将方法传递给子组件,子组件内接收到该方法后,可以直接调用

mine-form-item执行校验

min-form-item组件在mounted钩子里进行监听

import Validator from "async-validator";

inject: ["form"],
mounted() {
  this.$on("validate", () => {
    this.validate();
  });
},
methods: {
    validate() {
      const rules = this.form.rules[this.prop];
      const value = this.form.model[this.prop];
      const validator = new Validator({ [this.prop]: rules });
      return validator.validate({ [this.prop]: value }, (errors) => {
        if (errors) {
          this.error = errors[0].message;
        } else {
          this.error = "";
        }
      });
    },
  },
安装async-validator

async-validator也是element官方使用的的表单校验插件

安装命令:

npm i async-validator -S
validator方法

在该方法内部:

  • 首先通过inject方式接收到form实例
  • 根据自身props传递的prop属性找到当前值value和当前规则rule
  • 创建校验对象
  • 执行校验对象的校验方法,并给出校验结果和信息提示

mine-form提交前校验

from组件内创建validate方法,以供通过ref指向方式调用

form组件校验方法
validate(cb) {
  const results = this.$children
    .filter((item) => item.prop)
    .map((item) => item.validate());
  Promise.all(results)
    .then(() => cb(true))
    .catch(() => cb(false));
},
  • 通过this.$children获取到子组件数组形式的集合
  • 通过链式操作获取有prop属性的表单项,并执行对应的校验方法
  • promise.all()返回是否全部校验通过的结果。
promise.all方法

Promise.all可以将多个Promise实例包装成一个新的Promise实例。同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值。

调用form组件校验方法
submitForm() {
  this.$refs.loginForm.validate((valid) => {
    console.log(valid);
  });
},

以上就是我在网课学习的组件化实战内容。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

前端SkyRain

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值