001.前端问题-如何封装手机号验证的通用组件?

亚奇洛贝:

群主在吗 我在想有一个功能能不能实现 就是项目中有很多表单验证 其中有很多相同的 比如手机号的验证 能不能封装一个组件或者自定义指令 专门用来验证手机号 这样就不用在每个组件里面写一遍验证规则。

我的思路

Vue和React都是以组件化的方式来开发项目,所以充分利用组件化的思路来解决通用问题才能把这些框架的价值发挥出来。亚奇洛贝提到的这个功能需求也想到了要封装成通用验证组件的想法非常好。

那下面我们利用手机号验证组件为例子,来分析并实现一个手机号验证组件。

一、功能拆分

手机号验证组件需要哪些元素组成

InputPhoneNumber

  1. 手机号——标题 (title)
  2. 输入框
    1. 未填写内容时的提示——placeholder
    2. 输入框中输入内容——value
  3. 手机号输入错误时提示信息——errorTips

二、用到的Vue相关技术

  1. Vue组件封装component技术
    1. props 属性定义对外接口
    2. data 组件内状态数据
    3. method 组件内调用方法
  2. v-model
    1. 利用v-model的更新机制,让组件调用者方便绑定外部字段到组件内部,实现自动更新外部数据字段
    2. 如果对v-model的机制不了解,可以参考 Vue.js官方文档v-model的相关章节

三、动手封装组件

1.实现一个最简单的输入组件

最简单的方式调用InputPhoneNumber组件,一会我们来实现这个InputPhoneNumber组件

<!--App.vue父容器-->
<template>
	<InputPhoneNumber />  
</template>

<script>
import InputPhoneNumber from "./components/InputPhoneNumber";

export default {
  name: "App",
  data: function(){
    return {
      phoneNumber:'',
    }
  },
  components: {
    InputPhoneNumber
  },
};
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

封装InputPhoneNumber组件 v1版

调用端加入 v-model="phoneNumber"

<!--App.vue父容器-->
<InputPhoneNumber v-model="phoneNumber" />

<script>
import InputPhoneNumber from "./components/InputPhoneNumber";

export default {
  name: "App",
  data: function(){
    return {
      phoneNumber:'',
    }
  }
} 
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!--InputPhoneNumber.vue组件-->
<template>
  <div class="phoneContainer">
    <label>手机号:</label>
    <input 
      class="phoneNumber" 
    />
    <span class="tip">您输入的手机号格式有误</span>
  </div>
</template>
<script>
export default {
  name: "InputPhoneNumber",
};
</script>
<style scoped>
  .phoneContainer{
    display:inline-block;
  }
  .phoneNumber{
    outline:none;
    border:solid 1px #ccc;
    padding:5px 10px;
    border-radius: 5px;
  }
  .tip{
    padding:5px;
    font-size:12px;
    color:red;
  }
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

基本上就是把一个基本的手机号输入组件的HTML和CSS的样式布局写好

2.利用v-model让输入框功能和外部数据打通

<!--App.vue 容器-->
<InputPhoneNumber v-model="phoneNumber" />

<script>
import InputPhoneNumber from "./components/InputPhoneNumber";

export default {
  name: "App",
  data: function(){
    return {
      phoneNumber:'',
    }
  },
  components: {
    InputPhoneNumber
  },
};
</script>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

增加 v-model="phoneNumber" 和 data中的phoneNumber字段

<!--InputPhoneNumber.vue组件-->
<input 
      class="phoneNumber" 
      v-bind:value="value"
      v-on:input="$emit('input', $event.target.value)" 
/>
1
2
3
4
5
6

v-model上绑定的phoneNumber会传递到InputPhoneNumber组件内props中的value上,所以InputPhoneNumber组件内部利用v-on:input可以把外部的phoneNumber绑定到输入框上,输入框里一旦有输入变动,又会通过 v-on:input对外发送input事件,被外部的v-model捕获到,完成空间内的值更新到外部的phoneNumber的双向绑定过程。

结果就是你看到的输入框里有内容变化,外部的phoneNumber也会跟着变化了。

3.利用props来提取控件中的文字为可配置的属性

InputPhoneNumber

回头再来看看这个手机号输入控件,上面的手机号,请输入手机号,以及后面的错误提示文字。都是可以做成灵活配置的。因此,这些之前写死的文字都可以提取成一个个属性接口

<!--InputPhoneNumber.vue组件-->
<template>
  <div class="phoneContainer">
    <label>{{title}}</label>
    <input 
      class="phoneNumber" 
      v-bind:value="value"
      v-on:input="$emit('input', $event.target.value)" 
      :placeholder="placeholder" 
    />
    <span class="tip">{{errorTips}}</span>
  </div>
</template>

<script>
export default {
  name: "InputPhoneNumber",
  props: {
    title:{
      type: String,
      default: "手机号:",
    },
    errorTips:{
      default:"您输入的手机号格式有误",
      type: String,
    },
    placeholder:{
      default: "请输入您的手机号",
      type: String,
    },
    value: String,
  },
};
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

所以这一版的改动

  1. 手机号 提取成 title 属性
  2. 您输入的手机号格式有误 提取成 errorTips 属性
  3. 请输入您的手机号 提取成 placeholder 属性

这样改动后,以后调用InputPhoneNumber控件就可以自己指定这些部分了。

4.实现手机号验证功能

这里我利用输入框失去焦点的时候,作为触发验证的时机。

<!--InputPhoneNumber.vue组件-->
<input class="phoneNumber" 
       @blur="handleBlur" 
/>

<script>
  data:function(){
    return {
      isPass:true,
    }
  },
  methods:{
    handleBlur:function(){
      this.valid();
    },
    valid:function(){
      //验证是否符合要求
      let validPass = /1[3|5|7|8|][0-9]{9}/.test(this.value);
      this.isPass = validPass;
      console.log("valid-->",this.isPass);
    }
  }
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

在InputPhoneNumber组件中添加 @blur绑定handleBlur方法,这样一旦输入框失去焦点,就会触发handleBlur方法,handleBlur方法内部调用valid方法,完成对手机号完整性的验证。验证的结果会被更新到isPass字段上。

5.实现验证成功和验证失败的界面表现

<!--InputPhoneNumber.vue组件-->
<input 
      class="phoneNumber" 
      :class="{
        isError:!isPass
      }"
      v-bind:value="value"
      v-on:input="$emit('input', $event.target.value)" 
      :placeholder="placeholder" 
      @blur="handleBlur" 
/>
<span v-show="!isPass" class="tip">{{errorTips}}</span>

<style scoped>
  .phoneNumber.isError{
    border-color:red;
  }
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

isPass验证失败为false时,**v-show="!isPass"**就会展示 您输入的手机号格式有误的内容,同时会把输入框加上红色边框。

6.最后的完善

有些时候,右侧的提示文字并不需要,所以需要把右侧的提示做成可以让使用者自己来控制是否显示。因此,再增加一个isShowTips的属性来控制是否开启提示。

<!--InputPhoneNumber.vue组件-->
<span v-show="isShowTips && !isPass" class="tip">{{errorTips}}</span>

<script>
  isShowTips: {
      type: Boolean,
      default: false,
    },
</script>
1
2
3
4
5
6
7
8
9
<!--App.vue 容器-->
<InputPhoneNumber v-model="phoneNumber" :isShowTips="true" />

<!--完整的属性-->
<InputPhoneNumber 
        v-model="phoneNumber" 
        title="用户手机:" 
        placeholder="填写手机号" 
        errorTips="格式有误"
        :isShowTips="true" 
/>
1
2
3
4
5
6
7
8
9
10
11

至此,一个相对完整的 通用手机号验证控件 诞生了。

完整代码,参考 封装组件Demo