这篇文章主要讲解了“vue3怎么实现H5表单验证组件”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“vue3怎么实现H5表单验证组件”吧!
效果图
描述
基于
vue.js,不依赖其他插件或库实现;基础功能使用保持和 element-ui 一致,内部实现做了一些移动端差异的调整。
当前构建平台使用 uni-app 官方脚手架构建,因为当下移动端大多情况就
h6和
微信小程序两种,所以一套代码跑多端十分适合技术选型。
实现思路
核心api:使用 provide 和 inject ,对应
<form>和
<form-item>。
在
<form>组件中,内部用一个变量(数组)去将所有
<form-item>实例储存起来,同时把要传递的数据通过
provide暴露出去;
<form-item>组件则在内部用
inject去接收父组件提供过来的数据,最后把自身的属性和方法提交到父组件去。
<form>中要做的事情就只有监听绑定的数据,然后做调用对应
<form-item>的各种验证方法;所以任何的验证状态都写在了
<form-item>中,包括样式的展示;由于可以拿到父组件绑定的数据,对于一些常见样式设置自然就可以用
computed去优先拿取自身组件
prop值或者父组件绑定的
prop值;对应的,通过父组件绑定的表单数据,就可以和自身
prop去验证当前项了,最后由父组件去调用对应方法即可,当然,自身组件也可以调用。
因为
vue3中移除了自定义派发事件
$on、
$off、
$emit,所以这里改用
uni.$on、
uni.$off和
uni.$emit来代替;不同的是,这个事件派发机制是全局的,不是跟随组件唯一性,所以在添加、移除事件时,需要在事件名称设置一个唯一值使用;这里我在
<form>组件中定义一个变量,每次调用时都累加
1,然后设为事件名称再传递到
<form-item>内部,这样就可以保证
<form>和
<form-item>的确定性了。
与element-ui表单组件差异
表单验证不再设置输入框和任何表单表单的样式,而是通过自定义修改样式去显示验证提示;这十分有利于移动端穷出不尽的UI设计稿的变化,例如同一个表单,有两种不同样式的输入框;同时不影响和其他样式库的使用,因为表单验证的
<form-item>不会影响到插槽内的任何元素。
表单校验数据选项只保留
4个字段(见下面),因为移除了对表单组件的验证状态,所以
trigger这个事件设置也不需要了;
pattern则换成了
reg,注意的是,在微信小程序中,任何组件的传参都会被过滤剩下基础
json类型,所以这个
reg在小程序环境中使用时,要在末尾加上
.toString(),
validator同理。
/** 表单规则类型 */ export interface TheFormRulesItem { /** 是否必填项 */ required?: boolean /** 提示字段 */ message?: string /** 指定类型 */ type?: "number" | "array" /** * 自定义的校验规则(正则) * - 考虑到微信一些特殊的抽风机制,在微信小程序中,除`number|string|object|undefined|null`这几个基础类型外,其他类型是会被过滤掉,所以这里在写正则的时候,在末尾加上`.toString()`即可 */ reg?: string // | RegExp } /** 表单规则类型 */ export type TheFormRules = { [key: string]: Array<TheFormRulesItem> };
不知道大家在以往的长表单验证中,有没有遇到过点击验证之后,因为页面过长,所以不知道那个表单项校验不通过,从而需要翻阅定位到对应项;为了优化以往表单验证的体验,这里加入了验证之后,滚动到对应位置的操作,更加符合移动端的用户体验。
表单验证的触发机制:都知道
element-ui的触发机制是通过指定
trigger来选择触发的时机,那这里我去掉之后,就意味着没有这些操作去触发了;而我选择的是主动调用
validate、
validateField这些验证方法时去触发实时验证,当验证不通过时,把不通过的用变量储存起来,然后每次数据变动时去校验,等到验证通过了,则移除实时验证项;这样相比于
element-ui绑定事件实时去验证会节省很多代码调用和运行的机制,同时代码可以做到更高校和精简。
非uni-app平台的移植
除了更换标签之外,几乎不用做任何的修改就可以复制粘贴到其他项目中去,唯一要修改的就是自定义事件
uni.$on、
uni.$off和
uni.$emit;这里可以自己实现,又或者用其他库去代替,
js实现自定义事件派发代码如下:
function moduleEvent() { /** * 事件集合对象 * @type {{[key: string]: Array<Function>}} */ const eventInfo = {}; return { /** * 添加事件 * @param {string} name 事件名 * @param {Function} fn 事件执行的函数 */ on(name, fn) { if (!eventInfo.hasOwnProperty(name)) { eventInfo[name] = []; } if (!eventInfo[name].some(item => item === fn)) { eventInfo[name].push(fn); } }, /** * 解绑事件 * @param {string} name 事件名 * @param {Function} fn 事件绑定的函数 */ off(name, fn) { const fns = eventInfo[name]; if (fns && fns.length > 0 && fn) { for (let i = 0; i < fns.length; i++) { const item = fns[i]; if (item === fn) { fns.splice(i, 1); break; } } } else { console.log("[moduleEvent] => 没有要解绑的事件"); } }, /** * 调用事件 * @param {string} name 事件名 * @param {any} params 事件携带参数 */ dispatch(name, params) { const fns = eventInfo[name]; if (fns && fns.length > 0) { for (let i = 0; i < fns.length; i++) { const fn = fns[i]; fn(params); } } else { console.log("[moduleEvent] => 没有要执行的事件"); } }, } }
调用
moduleEvent()之后,用变量调用即可,注意当前变量要作为内存常驻使用。