Validate表单组件的封装

之前一直是直接去使用别人现成的组件库,也没有具体去了解人家的组件是怎么封装的,造轮子才会更好地提高自己,所以尝试开始从封装Form表单组件开始
一:组件需求分析
本次封装组件,主要是摸索封装组件的流程,对于具体需要的方法和属性,只会实现其中部分方法和属性,之后一点一点才进行添加

- 表单项组件,ValidateInput组件的封装
- 根据传递不同的type类型有着不同的校验规则
- 支持V-model,对于封装的自定义组件支持v-model双向绑定也是一个很关键的属性
- tag根节点的类型
- form组件,提交事件
- 验证整体表单项,是否通过
- 清空表单项的内容
二:技术栈
- Vue3
- TS
- Bootstrap样式库
三:封装Validate-input验证表单项组件
思路
1. 明确属性和事件
- v-model属性
- rules属性:根据不同type不同校验规则
- tag根节点类型
ValidateInput组件
<template><div class="validate-input-container pb-3"><inputclass="form-control":class="{ 'is-invalid': inputRef.error }":value="modelValue"@blur="validateInput"@input="updateValue"v-bind="$attrs"/><span v-if="inputRef.error" class="invalid-feedback">{{ inputRef.message }}</span></div>
</template>
<script setup lang="ts">
//禁用 Attributes 继承
defineOptions({inheritAttrs: false
})
//定义传来的一个参数类型
interface RangeProp {message: stringlength: number
}
interface RuleProp {type: 'required' | 'email' | 'range'message?: string//至少位数min?: RangePropmax?: RangeProp
}
//数组类型
export type RulesProp = RuleProp[]
//接收参数
const props = defineProps<{rules?: RulesProp//自定义组件使用v-model需要用modelValue来接收参数modelValue: string
}>()//定义表单的数据
const inputRef = reactive({//如果为空val: props.modelValue || '',error: false, //表单验证是否通过message: '' //错误信息
})
</script>
-
禁用Attributes继承:透传 attribute”指的是传递给一个组件,却没有被该组件声明为 props 或 emits 的 attribute 或者
v-on事件监听器。最常见的例子就是class、style和id。<!-- <MyButton> 的模板 --> <button>click me</button>当父组件使用该组件,并且传入class:
<MyButton class="large" />最终会在根元素出现class=‘large“,
<MyButton>并没有将class声明为一个它所接受的 prop,所以class被视作透传 attribute,自动透传到了<MyButton>的根元素上。因此需要禁用Attibutes继承
-
定义接收参数类型
- rules可选参数,接收数组
- modelValue:接收字符串即输入的值
2. v-model属性
在使用一个自定义组件给其添加v-model属性,其自定义组件内部做了两件事
- 将内部原生的input元素的值绑定到组件内部Prop属性modelValue
- 当原生input元素触发时候,触发一个携带了新值的
update:modelValue自定义事件
<!-- 自定义组件Validate-input --><Validate-input v-model="loginParams.email" :rules="emailRules" placeholder="请输入邮箱地址"></Validate-input>
Validate-input组件
//接收参数
const props = defineProps<{rules?: RulesProp//自定义组件使用v-model需要用modelValue来接收参数modelValue: string
}>()
props接收了,modelValue属性,类型string
<inputclass="form-control":class="{ 'is-invalid': inputRef.error }":value="modelValue"@blur="validateInput"@input="updateValue"v-bind="$attrs"/>
设置了自定义事件用于更新value
//手动更新value
const updateValue = (e: Event) => {//HTMLInputElement输入元素类型let targetValue = (e.target as HTMLInputElement).value//更新文本框的值inputRef.val = targetValueemit('update:modelValue', targetValue)
}
因此实现v-model属性
3. rules属性
- 接收参数
- type:校验类型,requied | email | range
- message
- min:至少几位
- max:至多几位
抽象验证逻辑
validate-input组件
//数组类型
export type RulesProp = RuleProp[]
//接收参数
const props = defineProps<{rules?: RulesProp//自定义组件使用v-model需要用modelValue来接收参数modelValue: string
}>()
参数接收rules是个数组
//定义表单的数据
const inputRef = reactive({//如果为空val: props.modelValue || '',error: false, //表单验证是否通过message: '' //错误信息
})
定义表单的数据
//定义事件
const validateInput = () => {//定义邮箱的正则let emailReg = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$///如果传过来的有验证的话if (props.rules) {//every方法 遍历全部数组只有全部满足条件才会返回true, 只要有一个false停止遍历let allRulePassed = props.rules.every(rule => {let passed = true//消息赋值 类型断言inputRef.message = rule.message as string//switch选择对应type类型错误进行校验switch (rule.type) {case 'required':if (inputRef.val.trim() === '') {passed = false}breakcase 'email':if (!emailReg.test(inputRef.val)) {passed = false}breakcase 'range':passed = validateRange(rule.min, rule.max)break}return passed})//allRulePassed为false表示通过//所以Input.error表示错误应该为trueinputRef.error = !allRulePassedreturn allRulePassed}return true
}
定义校验事件
-
满足全部rules校验才可以通过,因此用到es6数组方法,every只有全部项都为true才会遍历全部返回true,只要有一个项结果为false,就会停止遍历
-
message校验信息赋值给inputRef.message
-
利用swicth case语句,选择对应的type类型进行校验
-
需要充分考虑到所有情况,通过passed为true,未通过为fasle
-
range长度校验,由于情况较多,单独封装一个函数去校验-
//校验长度 const validateRange = (min: RangeProp | undefined, max: RangeProp | undefined) => {let passed = true//1. 如果min 存在 ,max不存在if (min && !max) {inputRef.message = min.messagepassed = !(inputRef.val.length < min.length)}//2. min不在, max在if (!min && max) {inputRef.message = max.messagepassed = !(inputRef.val.length > max.length)}//3. min在 max在if (min && max) {//若小于if (inputRef.val.length < min.length) {passed = falseinputRef.message = min.message}//若大于if (inputRef.val.length > max.length) {passed = falseinputRef.message = max.message}}return passed }
-
-
模板中,根据passed的值,动态绑定未通过的样式
4. 默认属性
validate-input组件
<div class="validate-input-container pb-3"><inputclass="form-control":class="{ 'is-invalid': inputRef.error }":value="modelValue"@blur="validateInput"@input="updateValue"v-bind="$attrs"/><span v-if="inputRef.error" class="invalid-feedback">{{ inputRef.message }}</span></div>
父组件
<Validate-inputv-model="loginParams.password":rules="passwordRules"type="password"placeholder="请输入密码"></Validate-input>
-
通过显示绑定attrs,使得自定义组件可以使用默认属性
-
在
ValidateInput组件中,可以通过$attrs属性将type属性将被传递到组件的根元素上 -
可以在父组件中灵活地传递任何HTML属性给
ValidateInput组件,使它更加通用和可配置
四:封装Validate-Form组件
- 事件
- form-submit:点击提交触发的事件,回调参数result是布尔值,表示该表单是否通过了校验
1. form-submit事件
- 当点击提交时,要去验证该表单是否通过了校验,就需要一个个将表单项进行校验,只要一个没通过就返回false,只有全部通过返回true
收集全部表单项校验函数
- 可以通过将所有表单项的校验函数都添加到一个数组中,然后最终通过every方法遍历是否全部通过
收集
安装mitt并使用,监听事件
npm install mitt --save
全局挂载
main.ts
import mitt from 'mitt'
//对外暴露全局事件总线实例
export const bus = mitt()
ValidateForm组件中使用
<script setup lang="ts">
import { onUnmounted, reactive } from 'vue'
import { bus } from '@/main'
const emits = defineEmits(['form-submit'])
//点击事件
//定义函数类型
type ValidateFunc = () => boolean
//定义类型
//定义接收的函数数组
const funcArr = reactive<ValidateFunc[]>([])
//测试回调
const callback = (func: ValidateFunc) => {//将每个校验函数加入数组funcArr.push(func)
}
//订阅事件
bus.on('form-item-created', callback as ValidateFunc)//提交按钮触发事件
const submitForm = () => {const result = funcArr.map(func => func()).every(result => result)// 触发提交事件//遍历funcArr中的每个校验函数emits('form-submit', result)
}
onUnmounted(() => {// 移除订阅bus.off('form-item-created', callback as ValidateFunc)
})
</script>
- 定义函数类型,返回值为空
- 利用泛型定义存放每个表单项校验函数存放的数组
- 订阅事件,在组件卸载的时候再取消订阅
ValidateInput组件中
- 组件挂载的时候,就直接触发自定义事件,然后将每一项的校验函数传递给
ValidateForm组件中
Validate-Form组件
- 然后接受
ValidateInput组件传递过来的校验函数,一个个加入到数组中 - 在submitForm事件中,遍历数组,不能直接使用every方法,因为只要一个不通过就不进行后面的校验,这是不满足我们的需求的
- 可以看到下面的错误实例,密码原本也是不通过校验的,但是every方法直接在第一个校验失败结束遍历了

- 因此先利用map函数,先使得每个校验函数都执行,结束后返回一个新的数组存放校验函数的返回值,再通过every遍历数组
- 最后触发自定义事件,将结果传递给父组件中
父组件
<Validate-Form @form-submit="submitForm" ref="validateFormRef">
const submitForm = (result: boolean) => {console.log(result)
}
提交表单元素后清空表单值
具体逻辑跟校验差不多
Validate-Form组件
<script setup lang="ts">
import { onUnmounted, reactive } from 'vue'
import { bus } from '@/main'
const emits = defineEmits(['form-submit'])
//点击事件
//定义函数类型
type ValidateFunc = () => boolean
//定义清空Input函数类型
type clearInputsFunc = () => void
//定义类型
//定义接收的函数数组
const funcArr = reactive<ValidateFunc[]>([])
//定义接收用于清空的函数数组
const clearFuncArr = reactive<clearInputsFunc[]>([])
//测试回调
const callback = (func: ValidateFunc) => {//将每个校验函数加入数组funcArr.push(func)
}
//事件回调
const clearInputFunc = (func: clearInputsFunc) => {clearFuncArr.push(func)
}
//绑定监听事件
bus.on('form-item-created', callback as ValidateFunc)
bus.on('form-item-clear', clearInputFunc as clearInputsFunc)//提交按钮触发事件
const submitForm = () => {const result = funcArr.map(func => func()).every(result => result)// 触发提交事件//遍历funcArr中的每个校验函数emits('form-submit', result)//遍历清空函数数组并依次并执行//当校验通过时候才会清空inputif (result) {clearFuncArr.map(func => func())}
}
onUnmounted(() => {// 移除事件监听器bus.off('form-item-created', callback as ValidateFunc)bus.off('form-item-clear', clearInputFunc as clearInputsFunc)
})
</script>
Validate-input组件
onMounted(() => {//直接把validateInput校验事件传递过去bus.emit('form-item-created', validateInput)//直接触发事件先传入每个input的值bus.emit('form-item-clear', clearInput)
})//定义表单的数据
const inputRef = reactive({//如果为空val: props.modelValue || '',error: false, //表单验证是否通过message: '' //错误信息
})
- 把清空表单数据的处理函数收集起来
- 最后利用map方法依次执行清除即可
五:演示和使用

在 vue template 中添加结构代码
<Validate-Form @form-submit="submitForm" ref="validateFormRef"><!-- 邮箱地址 --><div class="mb-3"><label for="exampleInputEmail1" class="form-label">邮箱地址</label><!-- 自定义组件Validate-input --><Validate-input v-model="loginParams.email" :rules="emailRules" placeholder="请输入邮箱地址"></Validate-input></div><!-- 密码 --><div class="mb-3"><label for="exampleInputPassword1" class="form-label">密码</label><Validate-inputv-model="loginParams.password":rules="passwordRules"type="password"placeholder="请输入密码"></Validate-input></div><template #submit><button type="submit" class="btn btn-primary btn-block btn-large">提交</button></template></Validate-Form>
在 setup语法糖中添加数据
//定义验证类型数据
const emailRules: RulesProp = [{ type: 'required', message: '电子邮箱地址不能为空' },{ type: 'email', message: '请输入正确的电子邮箱格式' }
]
//定义密码验证类型数据
const passwordRules: RulesProp = [{ type: 'required', message: '密码不能为空' },{type: 'range',min: { message: '你的密码至少包括6位,不能含有空格', length: 6 },max: {message: '你的密码至多15位,不能含有空格',length: 15}}
]//监听结果
const submitForm = (result: boolean) => {console.log(result)
}
Validate-Form属性和事件
- form-submit:
- 类型:事件
- 默认:-
- 说明:回调参数 (result: boolean) => void, result 代表是否通过了验证
Validate-Input属性和事件
- rules
- 类型:array
- 默认:-
- 说明: 单个输入框的验证类型,可以传入包含特定对象的数组, 详情可见上面示例代码
- tag
- 类型:input | textarea
- 默认:input
- 说明: 根节点类型
- v-model
- 类型: string
- 默认:-
- 说明: 支持 v-model,请对响应式数据
相关文章:
Validate表单组件的封装
之前一直是直接去使用别人现成的组件库,也没有具体去了解人家的组件是怎么封装的,造轮子才会更好地提高自己,所以尝试开始从封装Form表单组件开始 一:组件需求分析 本次封装组件,主要是摸索封装组件的流程,…...
企业架构LNMP学习笔记32
企业架构LB-服务器的负载均衡之LVS实现: 学习目标和内容 1)能够了解LVS的工作方式; 2)能够安装和配置LVS负载均衡; 3)能够了解LVS-NAT的配置方式; 4)能够了解LVS-DR的配置方式&…...
基于Jetty9的Geoserver配置https证书
1.环境准备 由于Geoserver自带的jetty版本不具备https模块,所以需要下载完整版本jetty。这里需要先查看本地geoserver对应的jetty版本,进入geoserver安装目录,执行如下命令。 java -jar start.jar --version Jetty Server Classpath: -----…...
企业互联网暴露面未知资产梳理
一、互联网暴露面梳理的重要性 当前,互联网新技术的产生推动着各种网络应用的蓬勃发展,网络安全威胁逐渐蔓延到各种新兴场景中,揭示着网络安全威胁不断加速泛化。当前网络存在着许多资产,这些资产关系到企业内部的安全情况&#…...
【动态规划刷题 12】等差数列划分 最长湍流子数组
139. 单词拆分 链接: 139. 单词拆分 给你一个字符串 s 和一个字符串列表 wordDict 作为字典。请你判断是否可以利用字典中出现的单词拼接出 s 。 注意:不要求字典中出现的单词全部都使用,并且字典中的单词可以重复使用。 示例 1: 输入: …...
react-redux 的使用
react-redux React Redux 是 Redux 的官方 React UI 绑定库。它使得你的 React 组件能够从 Redux store 中读取到数据,并且你可以通过dispatch actions去更新 store 中的 state 安装 npm install --save react-reduxProvider React Redux 包含一个 <Provider…...
77 # koa 中间件的应用
调用 next() 表示执行下一个中间件 const Koa require("koa");const app new Koa();app.use(async (ctx, next) > {console.log(1);next();console.log(2); });app.use(async (ctx, next) > {console.log(3);next();console.log(4); });app.use(async (ctx,…...
【css】z-index与层叠上下文
z-index属性用来设置元素的堆叠顺序,使用z-index有一个大的前提:z-index所作用元素的样式列表中必须有position属性并且属性值为absolute、relative或fixed中的一个,否则z-index无效。 层叠上下文 MDN讲解 我们给元素设置的z-index都是有一…...
系统架构设计师(第二版)学习笔记----多媒体技术
【原文链接】系统架构设计师(第二版)学习笔记----多媒体技术 文章目录 一、多媒体概述1.1 媒体的分类1.2 多媒体的特征1.3 多媒体系统的基本组成 二、多媒体系统的关键技术2.1 多媒体系统的关键技术2.2 视频技术的内容2.3 音频技术的内容2.4 数据压缩算法…...
【面试经典150 | 数组】合并两个有序数组
文章目录 写在前面Tag题目来源题目解读解题思路方法一:合并排序方法二:双指针方法三:原地操作-从前往后方法四:原地操作-从后往前 写在最后 写在前面 本专栏专注于分析与讲解【面试经典150】算法,两到三天更新一篇文章…...
系统架构设计专业技能 ·操作系统
现在的一切都是为将来的梦想编织翅膀,让梦想在现实中展翅高飞。 Now everything is for the future of dream weaving wings, let the dream fly in reality. 点击进入系列文章目录 系统架构设计高级技能 操作系统 一、操作系统概述二、进程管理2.1 进程概念2.2 进…...
CSP 202209-1 如此编码
答题 题目就是字多 #include<iostream>using namespace std;int main() {int n,m;cin>>n>>m;int a[n],c[n1];c[0]1;for(int i0;i<n;i){cin>>a[i];c[i1]c[i]*a[i];}for(int i0;i<n;i){cout<<(m%c[i1]-m%c[i])/c[i]<< ;} }...
windows安装向量数据库milvus
本文介绍windows下安装milvus的方法。 一.Docker安装 1.1docker下载 首先到Docker官网上下载docker:Docker中文网 官网 1.2.安装前前期准备 先使用管理员权限打开windows powershell 然后在powershell里面输入下面那命令,启用“适用于 Linux 的 Windows 子系统”…...
Qt中,QScript对JavaScript的内置接口支持情况
支持 JSON.parse()/stringify() Object.keys() 不支持 console.info()/debug()/warn()/error() window setTimeout() clearTimeout() setInterval() clearInterval() 后续添加更多接口支持情况~...
C语言基础-typedef的用法
文章目录 前言基础用法高阶用法typedef作用于数组typedef作用于函数指针 总结 前言 熟悉C语言的同学,应该都见过typedef,但可能对typedef的用法并不是真的了解。本文介绍几种typedef的用法,相信会有所帮助 基础用法 一般typedef用来声明一个…...
Linux中安装MySQL5.7.42
1. 首先,下载mysql5.7.42的安装包(下方是下载地址),选择红色框框的下载(注意的是,这个链接只提供5.7的版本下载,可能还会更新,不一定打开就是5.7.42的版本,后续可能会有4…...
网络基础--1.网络纵横
网络的发展历程 计算机由原来的只能单一处理信息(单用户批处理)逐步发展为多用户批处理,可以实现一台计算机连接多个终端同时使用一台计算机(分时系统),但是多个终端之间不能相互通信,再发展成为…...
Django TypeError: Abstract models cannot be instantiated.错误解决方案
问题 [2023-09-05 10:23:41][dvadmin.utils.exception.CustomExceptionHandler():64] [ERROR] Traceback (most recent call last): File “D:\InstallSpace\Anaconda3\envs\py39\lib\site-packages\rest_framework\views.py”, line 506, in dispatch response handler(requ…...
vscode使用delve调试golang程序
环境配置 delve仓库,含有教程:https://github.com/go-delve/delve golang的debugging教程:https://github.com/golang/vscode-go/wiki/debugging > go version go version go1.20 windows/amd64> go install github.com/go-delve/de…...
如何从任何苹果、Windows或安卓设备访问iCloud照片
本文介绍了如何在各种设备上访问iCloud照片库,包括iPhone和iPad、Mac、Windows PC和Android设备。说明适用于iOS 13及以上版本、iPadOS 13及以上、macOS Big Sur(10.16)和Catalina(10.15)、Windows 10或11以及Android 10。 从iPhone、iPod Touch和iPad访问iCloud照片 照…...
无损视频剪辑神器LosslessCut:3分钟学会零编码损耗的专业剪辑技巧
无损视频剪辑神器LosslessCut:3分钟学会零编码损耗的专业剪辑技巧 【免费下载链接】lossless-cut The swiss army knife of lossless video/audio editing 项目地址: https://gitcode.com/gh_mirrors/lo/lossless-cut 你是否还在为视频剪辑时画质损失而烦恼&…...
导师推荐!盘点2026年顶流之选的AI论文写作工具
一天写完毕业论文在2026年已不再是天方夜谭。2026年AI论文写作工具全面升级,实测提速超300%,覆盖选题构思、文献综述、数据整理、格式排版等核心场景,高效搞定论文不再是梦想。 一、全流程王者:一站式搞定论文全链路(一…...
手把手教你用NOAA气象数据做可视化分析(含常见字段解析与避坑指南)
手把手教你用NOAA气象数据做可视化分析(含常见字段解析与避坑指南) 气象数据可视化是理解气候模式、分析极端天气事件的重要工具。美国国家海洋和大气管理局(NOAA)提供的全球历史气候网络日数据(GHCN-Daily࿰…...
深入理解incubator-pagespeed-ngx配置:50个实用参数详解与最佳实践
深入理解incubator-pagespeed-ngx配置:50个实用参数详解与最佳实践 Apache incubator-pagespeed-ngx是一个强大的Nginx性能优化模块,能够自动优化网站资源,显著提升页面加载速度。无论你是网站管理员还是开发人员,掌握其配置参数…...
DAC高速线缆市场洞察:预计到2032年将增长至180.8亿元
据恒州诚思调研统计,2025年全球DAC高速线缆市场规模达66.60亿元,预计到2032年将增长至180.8亿元,2026-2032年复合增长率(CAGR)为14.7%。作为数据中心短距离互连的核心组件,DAC高速线缆凭借其低延迟、高可靠…...
嵌入式设备文件传输协议解析与实践
嵌入式设备文件传输协议深度解析与应用实践1. 文件传输协议概述1.1 传统串口文件传输协议Xmodem协议族作为经典的串口文件传输解决方案,在嵌入式领域已有数十年的应用历史。该协议通过串口实现设备间的可靠数据传输,采用校验和或CRC校验机制确保数据完整…...
Android音频输出流实战:从AudioFlinger到HAL层的完整调用链解析
Android音频输出流深度解析:从框架设计到硬件交互 1. Android音频系统架构概览 Android音频子系统采用分层设计,每一层都有明确的职责划分。理解这个架构是分析音频输出流的基础。 核心层级结构: 应用层:通过AudioTrack、MediaPla…...
基于Coqui TTS的高质量语音合成实战:从模型部署到生产环境优化
最近在做一个需要语音播报功能的小项目,之前用的一些在线TTS服务,要么费用不低,要么音质和速度达不到要求。于是把目光投向了开源方案,一番折腾后,发现 Coqui TTS 真是个宝藏。它不仅音质好,支持的语言和声…...
基于STM32CubeMX的AD9850驱动开发与频率合成实战
1. 从零开始认识AD9850与STM32CubeMX 第一次接触AD9850这个芯片时,我完全被它的性能震撼到了——这个比指甲盖还小的芯片,居然能产生0.0291Hz分辨率的信号!当时我正在做一个射频测试项目,需要生成精确的正弦波信号。市面上常见的…...
基于AI多因子与流动性模型的黄金再定价分析:4500关口修复后的“黄金坑”是否成立?
摘要:本文通过引入AI多因子定价模型,结合流动性压力识别算法、资金流向追踪系统与宏观变量建模,对黄金从5602美元回落至4099美元后的市场行为进行分析,重点解析抛售驱动逻辑、相关性漂移及4500美元关口的再定价机制。一、AI趋势重…...
