组件化开发 | vue3+ts封装Input组件(组件化开发和模块化开发)
前言
在组件化开发中,输入框组件在开发中也是一个常用的组件之一,但是每次单独为每一个input输入框添加样式、校验规则等有些繁琐,也不利于维护管理,那么这篇文章将用来记录学习封装带校验规则的ValidateInput输入框。实现的功能有:
支持自定义校验规则
校验效果及时反馈
父子组件数据双向绑定
另外一些可自定义的参数/api,例如占位符placeholder
、focus
、change
可由需求扩展
自定义校验规则接口
这里以required
必须项和email
邮箱校验规则为例,并且需要一个校验失败的提示信息message
interface RuleProp = { type: 'required' | 'email'; message: string; } export type RulesProp = RuleProp[] 复制代码
自定义ValidateInput组件
将输入的值动态绑定到inputRef.val
,在输入框失焦blur
时执行校验,并在校验失败后将反馈信息inputRef.message
展示在提示框中
<template> <div class="validate-input-container pb-3"> <input type="text" v-model="inputRef.val" class="form-control" @blur="validateInput" /> <div v-if="inputRef.error" class="form-text"> {{ inputRef.message }} </div> </div> </template> <script lang="ts"> import { defineComponent, PropType, reactive } from 'vue' interface RuleProp { type: 'required' | 'email'; message: string; } const emailReg = /^([a-zA-Z]|[0-9])(\w|\-)+@[a-zA-Z0-9]+\.([a-zA-Z]{2,4})$/ // 验证邮箱 export type RulesProp = RuleProp[] export default defineComponent({ props: { rules: Array as PropType<RulesProp>, modelValue: String }, setup(props, context) { const inputRef = reactive({ val: '', error: false, message: '' }) const validateInput = () => { if (props.rules) { const allPassed = props.rules.every((rule) => { let passed = true inputRef.message = rule.message switch (rule.type) { case 'required': passed = inputRef.val.trim() !== '' break case 'email': passed = emailReg.test(inputRef.val) break default: break } return passed }) inputRef.error = !allPassed } } return { inputRef, validateInput, updateValue } } }) </script> 复制代码
调用时传入校验规则
const emailRules: RulesProp = [ { type: 'required', message: '邮箱不能为空' }, { type: 'email', message: '请输入正确的邮箱格式' } ] <validate-input :rules="emailRules" /> 复制代码
效果:
样式上与我们平时遇到的校验格式有很大出入,不过这些会在后面补上
组件数据双向绑定
到目前为止我们的input框有一个问题是:父组件不能拿到ValidateInput框内输入的内容,不能做到使用v-model
双向绑定数据。我们都知道v-model
的原理是通过v-bind
绑定一个value值,结合@input
事件动态改变绑定的value值实现数据双向绑定,那么我们在组件中我们也可以通过这个原理在组件间实现数据双向绑定
我们要知道在父组件中传入
v-model
的值,在子组件中是通过modelValue
接收的
ValidateInput通过
modelValue
接收父组件v-model
绑定的值
// 子组件 props: { rules: Array as PropType<RulesProp>, modelValue: String; } 复制代码
给input框绑定value值和@input事件
<template> <div class="validate-input-container pb-3"> <input type="text" class="form-control" + :value="inputRef.val" + @input="updateValue" :placeholder="placeholder" @blur="validateInput" /> </div> </template> 复制代码
发射事件通知父组件修改
v-model
的值
const updateValue = (e: Event) => { const targetValue = (e.target as HTMLInputElement).value inputRef.val = targetValue context.emit('update:modelValue', targetValue) } 复制代码
整个过程我用下面这个图表示出来,其实就是一个父子组件通信的过程,这是因为子组件不能直接修改props值(props值只负责传递数据),而是需要通知父组件修改传过来的props值,进行数据更新,再拿到修改后的值。
自定义校验用户名的输入框
通过给ValidateInput加上校验成功/失败后的样式,来达到及时给用户反馈校验后信息的效果
<template> <div class="validate-input-container pb-3"> <input type="text" class="form-control" v-model="inputRef.val" @blur="validateInput" :class="{ 'is-invalid': inputRef.error, 'is-valid': !inputRef.error }" /> <span v-if="inputRef.error" class="invalid-feedback"> {{ inputRef.message }} </span> <span v-if="inputRef.success" class="valid-feedback"> yes </span> </div> </template> 复制代码
<template> <div class="validate-input-container pb-3"> <input type="text" class="form-control" v-model="inputRef.val" :placeholder="placeholder" @blur="validateInput" :class="{ 'is-invalid': inputRef.error, 'is-valid': inputRef.success }" /> <span v-if="inputRef.error" class="invalid-feedback"> {{ inputRef.message }} </span> </div> </template> <script lang="ts"> import { defineComponent, PropType, reactive } from 'vue' interface RuleProp { type: 'required' | 'email' | 'username' | 'only' message: string } const emailReg = /^1[3|4|5|7|8][0-9]{9}$/ const usernameReg = /......./ // 这里写入用户名的正则表达式 export type RulesProp = RuleProp[] export default defineComponent({ props: { placeholder: String, rules: Array as PropType<RulesProp> }, setup(props) { const emailRules: RulesProp = [ { type: 'required', message: '用户名不能为空' }, { type: 'username', message: '请输入正确格式的用户名' }, { type: 'only', message: '用户名已存在' } ] const inputRef = reactive({ val: '', success: true, error: false, message: '' }) const validateInput = () => { if (props.rules) { const allPassed = props.rules.every((rule) => { let passed = true inputRef.message = rule.message switch (rule.type) { case 'required': passed = inputRef.val.trim() !== '' break case 'username': passed = usernameReg.test(inputRef.val) break case 'only': // 校验用户名是否存在 default: break } return passed }) inputRef.error = !allPassed inputRef.success = allPassed } } return { inputRef, validateInput } } }) </script> 复制代码
作者:alexis
链接:https://juejin.cn/post/7027833439667617829