Vue快速实现通用表单验证的方法


Posted in Javascript onFebruary 24, 2020

本文开篇第一句话,想引用鲁迅先生《祝福》里的一句话,那便是:“我真傻,真的,我单单知道后端整天都是CRUD,我没想到前端整天都是Form表单”。这句话要从哪里说起呢?大概要从最近半个月的“全栈工程师”说起。项目上需要做一个城市配载的功能,顾名思义,就是通过框选和拖拽的方式在地图上完成配载。博主选择了前后端分离的方式,在这个过程中发现:首先,只要有依赖jQuery的组件,譬如Kendoui,即使使用了Vue,依然需要通过jQuery去操作DOM。其次,只有有通过Rozar生成的DOM,譬如HtmlHelper,Vue的双向绑定就突然变得尴尬起来,更不用说,Rozar中的@语法和Vue中的@指令相互冲突的问题,原本可以直接用v-for生成列表,因为使用了HtmlHelper,突然一下子变得厌恶起来,虽然Rozar语法非常强大,可我依然没有在JavaScript里写C#的热情,因为实在太痛苦啦Orz……

所以,想做好前后端分离,首先需要分离出一套前端组件库,做不到这一点,前后端分离就无从谈起,就像我们公司的项目,即使框架切换到.NET Core,可是在很长的一段时间里,我们其实还是再写MVC,因为所有的组件都是后端提供的HtmlHelper/TagHelper这种形式。我这次做项目的过程中,其实是通过jQuery实现了一部分组件,正因为如此,一个在前后端不分离时非常容易实现的功能,在前后端分离以后发现缺好多东西,就比如最简单的表单验证功能,即便你是在做一个新项目,为了保证产品在外观上的一致性,你还是得依赖老项目的东西,所以,这篇博客主要想说说前后端分离以后,Vue的时代怎么去做表单的验证。因为我不想测试同事再给我提Bug,问我为什么只有来自后端接口的验证,而没有来自前端页面的验证。我希望,在写下这篇博客之前,我可以实现和老项目一模一样的表单验证。如同CRUD之于后端,80%的前端都是在写Form表单,所以,这个事情还是挺有意思的。

最简单的表单验证

OK,作为国内最接“地气”的前端框架,Vue的文档可以说是相当地“亲民”啦!为什么这样说呢,因为其实在官方文档中,尤大已经提供了一个表单验证的示例,这个示例让我想起给某银行做自动化工具时的情景,因为这两者都是采用MVVM的思想,所以,理解起来是非常容易的,即:通过一个列表来存储错误信息,而这个错误信息会绑定到视图层,所以,验证的过程其实就是向这个列表里添加错误信息的过程。我们一起来看这个例子:

<div>
 <h2>你好,请登录</h2>
 <div>
  <form id="loginFrom">
   <div>
    <label>邮箱</label>
    <input type="text" class="form-control" id="inputEmail3" placeholder="Email" v-model="email">
    </div>
   </div>
   <div>
    <label>密码</label>
    <input type="password" class="form-control" id="inputPassword3" placeholder="Password" v-model="password">
   </div>
   <div>
    <button type="button" class="btn btn-default login" v-on:click="login()">登录</button>
   </div>
   <div v-if="errorList.length > 0">
    <div class="alert alert-danger" role="alert">{{errorList.join(';')}}</div>
   </div>
  </form>
 </div>
</div>
<script>
var vm = new Vue({
 el: '#loginFrom',
 data: {
  email: "",
  password: "",
  errorList: []
 },
 methods: {
  validate: function () {
   this.errorList = []
   if (this.email == '') {
    this.errorList.push('请输入邮箱');
   } else {
    var reg = /^([a-zA-Z]|[0-9])(\w|\-)+@[a-zA-Z0-9]+\.([a-zA-Z]{2,4})$/;
    if (!reg.test(this.email)) {
     this.errorList.push('请输入有效的邮箱');
    }
   }
   if (this.password == '') {
    this.errorList.push('请输入密码');
   } else {
    if (this.password.length < 6) {
     this.errorList.push('密码长度不得少于6位');
    }
   }

   return this.errorList.length <= 0;
  },
  login: function () {
   if (this.validate()) {
    alert('登录成功');
   }
  }
 }
});
</script>

为了排除无关内容对大家的影响,写这个例子的时候,博主排除了一切复杂的HTML结构和CSS样式,经过简单润色以后,这个例子的效果展示如下,果然GUI满足了人们颜控的一面,可让这个世界高速运行的是CLI,Bootstrap是博主这种“全栈工程师”的最爱之一。这种验证方式简直是人类本能的反应,可这恰好是最糟糕的一个例子,因为这个代码完全没法复用,可以想象得到,如果再继续增加针对密码强度,譬如大小写、数字等等的验证,这个代码会混乱成什么样子,所以,这是最简单的表单验证,同样是最糟糕的表单验证。

Vue快速实现通用表单验证的方法

基于jQuery的表单验证

其实,如果不是因为老项目依赖jQuery,而新项目在某些地方又需要和老项目保持一致,有谁会喜欢在Vue的世界里使用jQuery呢?因为数据驱动和事件驱动,真的是两种不同的思想,我就见过因为监听不到某个事件而花费一整天时间的人……所以,这里使用jQuery的表单验证插件jQuery Validation,目的只有一个,即实现博主对自己的承诺,做一个和老项目一模一样的表单验证。官方这个示例最大的问题是,它的检验逻辑扩展性比较差,后端同学对这个应该有所体会啦,譬如实际业务中常常有邮箱、手机号、非空、数字、正则等等的验证规则,而后端常常采用基于Attribute的验证或者是FluentValidation这样的库,所以,核心问题是,能不能定义相应的验证规则。接下来,我们通过jQuery的表单验证插件来实现验证。

通常情况下,jQuery Validation支持面向控件和面向代码两种验证方式。所谓面向控件,就是指在控件里添加类似requiredemailrange等等的扩展属性,jQuery Validation内置了十余种标准的验证规则,基本可以满足我们的日常使用。而面向代码,就是通过JavaScript来定义验证规则,这就非常符合Vue数据驱动的风格了,因为在JavaScript里一切皆是对象,而这些对象可以作为Vue中的数据来使用。自然而然地,在第一个示例的基础上,我们可以非常容易地扩展出基于jQuery的表单验证:

var vm = new Vue({
 el:'#loginFrom',
 data:{
  email:"",
  password:"",
  validators:{
   rules: {
    email: {
     required: true,
     email: true
    },
    password: {
     required: true,
     minlength: 6,
    }
   },
   messages:{
    email:{
     required:"请输入邮箱",
     email:"请输入有效的邮箱"
    },
    password:{
     required:"请输入密码",
     minlength:"密码长度不得少于6位"
    }
   }
  }
 },
 mounted:function(){
  $('#loginFrom').validate(this.validators);
 }
});

对于当前表单loginFrom,其验证规则为validators,它完全参照jQuery Validation的API文档而来,具体大家可以从jQuery Validation的文档来做进一步了解。这里唯一看起来不爽的就是#loginFrom,因为它和整个Vue看起来格格不入。不过,像博主目前项目的处境,如果老项目里使用jQuery来对表单进行验证,而使用Vue开发的新项目要兼容老项目的设计风格,使用jQuery有什么不可以呢?不得不说,Vue作为一个渐进式的开发框架,真正照顾了各个"年龄"段的前端工程师。使用jQuery Validation以后的表单验证效果如下:

Vue快速实现通用表单验证的方法

通过jQuery Validation,我们或许能感觉到一点不一样的地方,那就是表单验证其实还是蛮有意思的哈。也许是因为我原本是一个无聊的人,所以看到一点新的东西就觉得有趣。就像我虽然在提交数据时在后端做了校验,可牺牲的其实是整个前端的使用体验。而如果在前端对数据进行校验,是在输入过程中校验还是在输入完成校验,是通过表单自带的提交功能还是自己发起一个AJAX请求,这里面的确是有非常多的细节支撑的。第一种方案不支持远程校验,这更加能说明校验本身要考虑的不单单只有前端了,同理,有了前端的校验,不代表后端可以不做校验。前端时间有人在知乎上提问,大意是说前端该不该完全信任后端返回的数据,严格来说,我们不应该信任任何人提供的数据,而这就是校验这件事情本身的意义。

基于Vue的表单验证

OK,如果说前面的两种校验是因为我们有一点历史包袱,那么,接下来,我们将尝试采用更“现代化”的表单验证方式。通过Vue文档中关于数据校验这一节的内容,我们了解到官方推荐的两个表单验证插件是vuelidate和VeeValidate,而实际上这篇博客中的第一个例子,就是由文档中的例子演化而来。我个人比较喜欢后者,所以,下面我们将使用这个插件来完成第三个例子。首先 ,我们通过Vue-Cli创建一个Vue项目,然后安装下面vee-validatevue-i18n两个插件:

npm install vee-validate@2.0.0 --save
npm install vue-i18n

注意到这里指定了版本号,这是因为最新的3.x超出了我这个新人的接受范围,一句话,太难了!接下来,我们在入口文件main.js中添加下面的代码,目的是启用这两个插件:

import VueI18n from 'vue-i18n';
import VeeValidate from 'vee-validate';
import zh_CN from 'vee-validate/dist/locale/zh_CN'

//启用Vue国际化插件
Vue.use(VueI18n)

//配置VeeValidate
const i18n = new VueI18n({
 locale: 'zh_CN',
})

Vue.use(VeeValidate, {
 i18n,
 i18nRootKey: 'validation',
 dictionary: {
  zh_CN
 }
});

接下来,编写一个单文件组件LoginForm.vue:

<!-- template of LoginForm -->
<template>
 <div class="container">
 <h2 class="text-center">你好,请登录</h2>
 <div class="row">
  <form class="form-horizontal col-md-offset-4 col-md-4" id="loginFrom">
  <div class="form-group">
   <label for="inputEmail3" class="col-sm-2 control-label">邮箱</label>
   <div class="col-sm-10">
   <input type="text" class="form-control" id="email" name="email" placeholder="Email" v-model="email" v-validate="'required|email'" data-vv-as="邮箱"/>
   <p class="alert alert-danger" role="alert" v-show="errors.has('email')">{{ errors.first('email') }}</p>
   </div>
  </div>
  <div class="form-group" name="password" rules="required">
   <label for="inputPassword3" class="col-sm-2 control-label">密码</label>
   <div class="col-sm-10">
   <input type="password" class="form-control" id="password" name="password" placeholder="Password" v-model="password" v-validate="'required|min:6'" data-vv-as="密码"/>
   <p class="alert alert-danger" role="alert" v-show="errors.has('password')">{{ errors.first('password') }}</p>
   </div>
  </div>
  <div class="form-group">
   <div class="col-sm-offset-2 col-sm-10">
   <div class="checkbox">
    <label>
    <input type="checkbox" />记住密码
    </label>
   </div>
   </div>
  </div>
  <div class="form-group">
   <div class="col-sm-offset-2 col-sm-10">
   <button type="button" class="btn btn-default login" v-on:click="login()">登录</button>
   </div>
  </div>
  </form>
 </div>
 </div>
</template>
<!-- script of LoginForm -->
<script>
export default {
 name: "LoginForm",
 components: {},
 data: () => ({
 email: "",
 password: ""
 }),
 methods: {
 login: function() {

 }
 }
};
</script>
<!-- style of LoginForm -->
<style scoped>
.login {
 color: white;
 height: 38px;
 width: 300px;
 background-color: #2b669a;
}
</style>

可以看到,我们在关键的两个input控件上添加了v-validatedata-vv-as这两个属性。比如我们这里需要验证用户输入的邮箱是否合法、邮箱是否为空,那么我们就可以使用下面的语法:

<input type="text" class="form-control" id="email" name="email" placeholder="Email" v-model="email" v-validate="'required|email'" data-vv-as="邮箱"/>
<p class="alert alert-danger" role="alert" v-show="errors.has('email')">{{ errors.first('email') }}</p>

这些语法在Vue中被称为指令,而data-vv-as则是HTML5中的一个特性,用来给提示信息中的字段起一个别名。实际上,这个插件里同样内置了一批常见的校验规则。当控件中的值不满足校验条件时,就会在errors中产生错误信息,所以,我们根据错误信息中是否包含指定字段来决定要不要展示错误信息,这就是这个插件的作用。运行这个例子,我们会得到下面的结果。

Vue快速实现通用表单验证的方法

既然提到这类表单验证最难的地方在于扩展性,那么下面我们再来看看如何扩展一个新的校验规则,这里以最常见的手机号校验为例, 个人以为这是这个插件最为强大的地方:

Validator.extend('isMobile', {
 messages: {
 zh_CN: field => field + '必须是11位手机号码'
 },
 validate: value => {
 return value.length === 11 && /^((13|14|15|17|18)[0-9]{1}\d{8})$/.test(value)
 }
})

相信通过今天这篇博客,大家应该对Vue里的表单验证有一点心得了。这类验证的库或者框架其实非常多,整合到Vue中要做的工作无外乎写一个插件,在控件触发相关事件或者表单提交的时候进行验证。作为一个Vue的新人,这个过程可谓是路漫漫其修远。你大概想不到,我是在凌晨加班加到凌晨两点半的情况下做完这几个示例的,最近这两三个月里加的班比我过去三年都多,这到底是好事还是坏事呢?有时候不知道自己还能不能坚持下去,往事已矣,人难免会感到迷茫的吧!

本文小结

这篇博客主要通过三个示例分享了Vue下表单校验的实现,而促使博主对这一切进行研究的原始动力,则是源于一个实际工作中通过Vue开发的新项目。前后端要不要分离、项目里要不要继续使用jQuery、该不该频繁地操作DOM,这其实是毫无关联地三件事情,而这种事情90%的人是完全不关心的,就像有一种看起来相当“成年人”的做法,出了事情第一时间不是去纠结谁的过错,而是问能不能马上解决以及解决问题需要多长时间。这看起来好像一点问题都没有,可不去在意事件本身对错的人,是因为这些问题不需要他去处理,利益相关和责任相关是完全不一样的,因为你不能一出问题全部都找到程序员这里,这项目又不是程序员一个人的。我关心这些无关紧要的问题,纯粹是因为我对自己做的东西有一种感情,我想做好它而已,我希望自己是个纯粹的人,而且可以一直纯粹下去,晚安!

Javascript 相关文章推荐
文本链接逐个出现的js脚本
Dec 12 Javascript
javascript中一些util方法汇总
Jun 10 Javascript
浅谈JavaScript字符串拼接
Jun 25 Javascript
jQuery实现监控页面所有ajax请求的方法
Dec 10 Javascript
bootstrap日期控件问题(双日期、清空等问题解决)
Apr 19 Javascript
详解react-router如何实现按需加载
Jun 15 Javascript
jQuery中each循环的跳出和结束实例
Aug 16 jQuery
浅谈AngularJs 双向绑定原理(数据绑定机制)
Dec 07 Javascript
详解微信小程序框架wepy踩坑记录(与vue对比)
Mar 12 Javascript
小程序云开发教程如何使用云函数实现点赞功能
May 18 Javascript
Vue实现穿梭框效果
Sep 30 Javascript
vue实现Toast组件轻提示
Apr 10 Vue.js
微信小程序后端实现授权登录
Feb 24 #Javascript
Node使用Nodemailer发送邮件的方法实现
Feb 24 #Javascript
原生javascript实现类似vue的数据绑定功能示例【观察者模式】
Feb 24 #Javascript
Vue 技巧之控制父类的 slot
Feb 24 #Javascript
原生javascript的ajax请求及后台PHP响应操作示例
Feb 24 #Javascript
在 Vue 中编写 SVG 图标组件的方法
Feb 24 #Javascript
原生javascript中this几种常见用法总结
Feb 24 #Javascript
You might like
不用GD库生成当前时间的PNG格式图象的程序
2006/10/09 PHP
PHP求最大子序列和的算法实现
2011/06/24 PHP
兼容PHP和Java的des加密解密代码分享
2014/06/26 PHP
PHP中的日期加减方法示例
2014/08/21 PHP
php ucwords() 函数将字符串中每个单词的首字符转换为大写(实现代码)
2016/05/12 PHP
js中几种去掉字串左右空格的方法
2006/12/25 Javascript
获取css样式表内样式的js函数currentStyle(IE),defaultView(FF)
2011/02/14 Javascript
基于jquery的跟随屏幕滚动代码
2012/07/24 Javascript
JSON语法五大要素图文介绍
2012/12/04 Javascript
可简单避免的三个JS发布错误的详细介绍
2013/08/02 Javascript
JQuery each()嵌套使用小结
2014/04/18 Javascript
javascript常见操作汇总
2014/09/03 Javascript
jquery 实现两Select 标签项互调示例代码
2014/09/25 Javascript
jQuery中prev()方法用法实例
2015/01/08 Javascript
jQuery简单实现遍历单选框的方法
2017/03/06 Javascript
JS实现的模仿QQ头像资料卡显示与隐藏效果
2017/04/07 Javascript
[01:38]完美世界DOTA2联赛PWL S3 集锦第四期
2020/12/21 DOTA
Python基于回溯法子集树模板解决旅行商问题(TSP)实例
2017/09/05 Python
python 3.6 tkinter+urllib+json实现火车车次信息查询功能
2017/12/20 Python
详解Python函数式编程—高阶函数
2019/03/29 Python
浅析Windows 嵌入python解释器的过程
2019/07/26 Python
Python使用mongodb保存爬取豆瓣电影的数据过程解析
2019/08/14 Python
python构建指数平滑预测模型示例
2019/11/21 Python
配置python的编程环境之Anaconda + VSCode的教程
2020/03/29 Python
Python3爬虫中Splash的知识总结
2020/07/10 Python
python3 循环读取excel文件并写入json操作
2020/07/14 Python
美国著名的女性内衣零售商:Frederick’s of Hollywood
2018/02/24 全球购物
Moda Operandi官网:美国奢侈品电商,海淘秀场T台同款
2020/05/26 全球购物
质检员的岗位职责
2013/11/15 职场文书
办公室主任主任岗位责任制
2014/02/11 职场文书
党员民主生活会对照检查材料思想汇报
2014/09/28 职场文书
本溪水洞导游词
2015/02/11 职场文书
志愿者个人总结
2015/03/03 职场文书
公安机关起诉意见书
2015/05/20 职场文书
行政上诉状范文
2015/05/23 职场文书
警用民用对讲机找不同
2022/02/18 无线电