使用vue自定义指令开发表单验证插件validate.js


Posted in Javascript onMay 23, 2019

这段时间在进行一个新项目的前期搭建,新项目框架采用vue-cli3和typescirpt搭建。因为项目比较轻量,所以基本没有使用额外的ui组件,有时候我们需要的一些基础组件我就直接自己开发了。今天就来介绍一下如何利用vue的自定义指令directive来开发一个表单验证插件的过程。

1.vue插件开发

关于vue的插件开发,官方文档里有很清晰的说明,详情可以去阅读开发文档。我自己开发的表单验证插件validate.ts和loading,messageBox插件都是利用了这种方式。今天先来看表单验证插件的开发。

vue全局指令

// myPlugin.js
export default {
 install: (Vue, options) => {
 // 注册一个my-directive指令
 Vue.directive('my-directive', {
  bind(el, binding, vnode, oldVnode) {
  // 逻辑
  }
  ...
 })
 }
}
// main.js
import Vue from 'vue';
import myPlugin from 'myPlugin';
Vue.use(myPlugin);

上面是注册一个vue指令插件的写法。值得注意的是注册自定义指令的时候,bind()函数为指令的钩子函数,其中的参数el表示指令绑定的元素,可以直接操作DOM。binding表示一个对象,包括指令名称,绑定值等信息。vnode和oldVnode表示Vue编译生成的虚拟节点。

我们通过注册一个全局指令v-validateParams指令,绑定到输入表单的input标签上来校验当前输入值是否符合要求。

2.v-validateParams指令

最开始我参考了网上的一些代码。基础的实现如下:

整体框架

import Vue from 'vue'
export default {
 install: (Vue, options) => {
 // 注册一个全局自定义指令 `v-validateParams`
 Vue.directive('validateParams', {
  // 当被绑定的元素插入到 DOM 中时
  inserted: function (el, binding, vNode) {
  // 给指令绑定的Dom元素添加事件监听,监测输入框失焦事件
  // 每次当表单中的输入框失焦时执行函数
  el.addEventListener('blur', function (event) {
   // 1.首先重置所有错误提示
   // 2.获取自定义指令中传入的校验规则参数和表单输入的值
   // 3.依次判断当前输入的值是否符合校验规则
  })
  }
 })
 // 注册一个全局自定义指令 `v-validateSubmit`,这个指令绑定到表单的提交button上
 Vue.directive('validateSubmit', {
  // 当被绑定的元素插入到 DOM 中时
  inserted: function (el, binding, vNode) {
  // 给提交button添加事件监听
  el.addEventListener('click', function (event) {
   // 获取当前组件内所有含有v-check类名的元素
   let elements = vNode.context.$el.getElementsByClassName('v-check')
   var evObj = vNode.context.$el.createEvent('Event')
   evObj.initEvent('blur', true, true)
   for (let element of elements) {
   // 给所有v-check元素绑定blur事件
   element.dispatchEvent(evObj);
   }
   // 获取当前组件下的所有错误提示元素
   let errorInputs = vNode.context.$el.getElementsByClassName('input-error');
   // 如果组件中没有错误提示元素,则执行当前组件实例中的submit()函数
   if(errorInputs.length === 0){
   vNode.context.submit();
   }
  })
  }
 })
 }
}

这里需要着重说明一下 validateSubmit 指令,这个指令绑定到提交按钮上,在点击的时候执行校验,校验通过之后执行提交操作。但是这里的实现方式不是特别友好:

1.需要获取当前组件中的所有input元素,给他们绑定并执行 blur 事件,以此来执行 validateParams 指令中的校验逻辑。

2.需要获取当前组件中的所有错误提示元素,如果他们存在就不能执行提交操作。

3.当组件内不含任何错误提示元素时,就表示校验通过,执行当前组件内的 submit 函数,所以每个表单组件的提交函数都只能命名为 submit

然后我们再看下指令 validateParams ,该指令需要绑定到表单 input 元素上,并把校验规则当作参数写入。当该input元素失焦时,会执行指令中给当前元素绑定的事件中的逻辑。这些逻辑分为三个步骤,我已经写在注释里了,现在我们来看下具体实现。

重置所有错误提示

/**
 * 重置当前节点样式
 * @param el: HTMLElement,传入当前绑定的input元素
 */
const resetError = (el: HTMLElement) => {
 el.className = el.className.replace('input-error', '').trim();
 if ( el.parentNode ) {
 const ErrorNode = el.parentNode.querySelector('.error-tips');
 if (ErrorNode) {
  el.parentNode.removeChild(ErrorNode);
 }
 }
};

获取自定义指令中传入的校验规则参数和表单输入的值

// binding.value是传入自定义指令的参数,以数组的形式
for (const rule of binding.value) {
 // 分别获取到自己定义的校验规则并执行
 const { min, max, message, required, pattern } = rule;
 if ( min && InputEl.value.length < min ) {
 // 如果不符合校验,执行报错函数
 validateError(InputEl, message);
 break;
 }
 if ( max && InputEl.value.length > max ) {
 validateError(InputEl, message);
 break;
 }
 if ( !!required && !InputEl.value ) {
 validateError(InputEl, message);
 break;
 }
 if ( pattern && !pattern.test(InputEl.value) ) {
 validateError(InputEl, message);
 break;
 }
 if ( rule && typeof rule === 'function' ) {
 rule(vNode.context, InputEl.value, validateError, InputEl);
 break;
 }
}

校验不符合,执行报错函数

/**
 * 执行错误提示函数,用input-error 类名和含有错误信息的p元素表示未通过校验
 * @param el: HTMLElement,传入当前绑定的input元素
 * @param errorMsg: string,传入错误提示信息
 */
const validateError = (el: HTMLElement, errorMsg: string) => {
 if (Array.prototype.includes.call(el.classList, 'input-error')) {
 //如果当前组件里已经有了错误提示信息,什么也不做
 return;
 } else {
 const errorNode = document.createElement('p');
 errorNode.className = 'error-tips';
 errorNode.textContent = errorMsg;
 if (el.parentNode) {
  // 在当前input 元素后追加一个p元素,内容为错误提示
  el.parentNode.appendChild(errorNode);
 }
 // 在当前input 元素上添加一个input-error类名
 el.className += ' input-error';
 }
};

现在我就把自己实现的这个表单校验插件大致说完了,下面我们看下具体使用。

3.自定义校验指令v-validateParams使用

首先新建校验规则文件:

// rules.ts
export const required = (message) => ({
 message,
 required: true
});
export const min = (message, length=3) => ({
 message,
 min: length
})
export const max = (message, length=15) => ({
 message,
 max: length
})
export const pattern = (message, reg) => ({
 message,
 pattern: reg
})
// form.vue
<template>
 <div>
 <div class="form-item">
  <label for="userEmail">用户名:</label>
  <input id="userEmail" class='v-check' type="text" v-model="userName"
  v-validateParams="[inputNameRequired, inputNameMin, inputNameMax, inputNamePattern]">
 </div>
 <button class="btn" v-if="show" type="success" v-checkSubmit>确认</button>
 </div>
</template>
<script lang='ts'>
import { Component, Vue, Prop } from 'vue-property-decorator';
import { max, min, required, name, pattern} from 'rules';

@Component({
 components: {},
})
export default class Auth extends Vue {
 private show: boolean = true;
 private userName: string = '';
 private inputNameMax = max('请不要超过20个字符');
 private inputNameMin = min('请不要小于3个字符');
 private inputNameRequired = required('请输入用户名');
 private inputNamePattern = pattern('请输入符合要求的用户名', /^[a-zA-Z0-9_-]{4,16}$/);
 private submit() {
 alert('通过校验');
 }
}
</script>

通过这个例子我们可以看到,使用时需要将校验规则引入并赋给vue实例中的数据。然后在模板中,需要给 input 标签添加 v-check 类名,再使用 v-validateParams 指令,并传入参数。提交按钮需要调用 v-checkSubmit 指令。按照这种方式就能够使用自己开发的这个表单校验插件。

3. 当前方式存在的问题

虽然表单校验可以使用了,但是存在一些显而易见的问题:

1.js和html耦合度较高,插件还需要获取dom元素,组件的html模板中还需要添加指定的类名。

2.在vue中使用dom操作,不符合vue的设计思路,实现方式也不优雅。

3.校验规则的校验逻辑在指令定义时写定了,添加或删除都需要改动插件代码。

4.提交指令根据当前组件内的是否含有特定dom来判断当前校验状态,且执行提交的函数名称也在指令逻辑中写定了。

我根据现有一个demo结合着自己的需求来实现的这个表单校验插件,开发的过程中我已经知道这么写问题很多,甚至不能称之为一个合格的插件。同时也清楚的认识到自己的javascript水平还很初级,需要很大进步。

当前开发的表单插件的主要问题在于如何将插件中的校验状态返回到组件内。我们可以在插件内维护一个事件处理函数,将校验规则传入并校验,再将校验结果直接传给组件内。这样就可以避免大量的dom操作。之后我需要尽快对这个插件进行更科学合理的重构。

总结

以上所述是小编给大家介绍的使用vue自定义指令开发表单验证插件validate.js,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
用Javascript实现锚点(Anchor)间平滑跳转
Sep 08 Javascript
基于jQuery的js分页代码
Jun 10 Javascript
使用JavaScript构建JSON格式字符串实现步骤
Mar 22 Javascript
javascript操作数组详解
Dec 17 Javascript
JavaScript中document.forms[0]与getElementByName区别
Jan 21 Javascript
Seajs是什么及sea.js 由来,特点以及优势
Oct 13 Javascript
纯js实现倒计时功能
Jan 06 Javascript
Electron 调用命令行(cmd)
Sep 23 Javascript
小程序组件传值和引入sass的方法(使用vant Weapp组件库)
Nov 24 Javascript
react中hook介绍以及使用教程
Dec 11 Javascript
JavaScript 数组去重详解
Sep 15 Javascript
浅析JavaScript中的变量提升
Jun 01 Javascript
微信小程序开发实现消息推送
Nov 18 #Javascript
jQuery操作attr、prop、val()/text()/html()、class属性
May 23 #jQuery
js打开word文档预览操作示例【不是下载】
May 23 #Javascript
仿iPhone通讯录制作小程序自定义选择组件的实现
May 23 #Javascript
判断js数据类型的函数实例详解
May 23 #Javascript
JS定义函数的几种常用方法小结
May 23 #Javascript
vue-test-utils初使用详解
May 23 #Javascript
You might like
PHP多线程抓取网页实现代码
2010/07/22 PHP
php根据日期判断星座的函数分享
2014/02/13 PHP
Laravel框架中实现使用阿里云ACE缓存服务
2015/02/10 PHP
php函数连续调用实例分析
2015/07/30 PHP
PHP获取文本框、密码域、按钮的值实例代码
2017/04/19 PHP
php设计模式之适配器模式原理、用法及注意事项详解
2019/09/24 PHP
PHP code 验证码生成类定义和简单使用示例
2020/05/27 PHP
javascript-简单的日历实现及Date对象语法介绍(附图)
2013/05/30 Javascript
jquery实现图片滚动效果的简单实例
2013/11/23 Javascript
javascript实现左右控制无缝滚动
2014/12/31 Javascript
JavaScript中两个字符串的匹配
2016/06/08 Javascript
自学实现angularjs依赖注入
2016/12/20 Javascript
JS中的phototype详解
2017/02/04 Javascript
JS异步函数队列功能实例分析
2017/11/28 Javascript
vue中实现先请求数据再渲染dom分享
2018/03/17 Javascript
node.js中Buffer缓冲器的原理与使用方法分析
2019/11/23 Javascript
微信公众号网页分享功能开发的示例代码
2020/05/27 Javascript
微信小程序学习总结(二)样式、属性、模板操作分析
2020/06/04 Javascript
[00:27]DOTA2战队VP、Secret贺新春
2018/02/11 DOTA
Python3.x和Python2.x的区别介绍
2013/02/12 Python
关于Python 3中print函数的换行详解
2017/08/08 Python
python接口自动化(十七)--Json 数据处理---一次爬坑记(详解)
2019/04/18 Python
Python 常用模块 re 使用方法详解
2019/06/06 Python
python科学计算之narray对象用法
2019/11/25 Python
基于PyTorch中view的用法说明
2021/03/03 Python
意大利在线大学图书馆:Libreria universitaria
2019/07/16 全球购物
澳大利亚在线高跟鞋商店:Shoe Me
2019/11/19 全球购物
工程管理造价应届生求职信
2013/11/13 职场文书
毕业自我鉴定怎么写
2014/03/25 职场文书
中秋寄语大全
2014/04/11 职场文书
2015新员工试用期工作总结
2014/12/12 职场文书
考试作弊检讨书怎么写?
2014/12/21 职场文书
放射科岗位职责
2015/02/14 职场文书
2015年电气技术员工作总结
2015/07/24 职场文书
vue+element ui实现锚点定位
2021/06/29 Vue.js
SQL Server远程连接的设置步骤(图文)
2022/03/23 SQL Server