首页 > 文章列表 > vue3怎么实现H5表单验证组件

vue3怎么实现H5表单验证组件

Vue3 h5
497 2023-05-10

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.$onuni.$offuni.$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来选择触发的时机,那这里我去掉之后,就意味着没有这些操作去触发了;而我选择的是主动调用validatevalidateField这些验证方法时去触发实时验证,当验证不通过时,把不通过的用变量储存起来,然后每次数据变动时去校验,等到验证通过了,则移除实时验证项;这样相比于element-ui绑定事件实时去验证会节省很多代码调用和运行的机制,同时代码可以做到更高校和精简。

非uni-app平台的移植

除了更换标签之外,几乎不用做任何的修改就可以复制粘贴到其他项目中去,唯一要修改的就是自定义事件uni.$onuni.$offuni.$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()之后,用变量调用即可,注意当前变量要作为内存常驻使用。