在 Vue.js 中,表单验证是常见的需求,而 vee-validate 库提供了一个简单而强大的解决方案。随着 vee-validate 从 3.x 版本升级到 4.x 版本,很多 API 和使用方式发生了变化。本文将带你从头到尾了解这两个版本的用法
Vee-Validate 简介
vee-validate 是一个专为 Vue.js 设计的表单验证库,它提供了灵活的验证规则、表单状态管理和错误信息展示等功能,支持 Vue 2 和 Vue 3。
为什么要学习 vee-validate?
- 易于集成:与 Vue.js 深度集成,表单验证非常简洁。
- 规则扩展:内置了大量的验证规则,支持自定义规则。
- 实时验证:支持表单控件的实时验证。
- 支持异步验证:可以与后端交互进行异步验证。
版本差异概览
在 vee-validate@3 和 vee-validate@4 版本中,主要的变化集中在:
- API 设计:从基于选项式的 API(如
useForm,useField)改为 Composition API 风格。 - 更简化的语法:通过 Vue 3 的
reactive和ref,使表单验证更加灵活。 - 验证规则的引入方式:4.x 改进了验证规则的引入和使用方式。
Vee-Validate 3.x 版本使用
安装和基础配置
npm install vee-validate@3
在main.js引入
import Vue from 'vue'
import App from '@/App.vue'
import router from '@/router/index.js'
import { ValidationProvider, ValidationObserver, extend } from 'vee-validate'
import { required, max, min, email } from 'vee-validate/dist/rules'
import validate from '@/utils/validate.js'
// 必填校验
extend('required', {
...required, // 官方自带的校验规则
message: '{_field_}为必填项!' // 自定义错误消息
})
// 邮箱校验规则
extend('email', {
...email,
message: `邮箱格式不正确`
})
console.log('min', min.params)
extend('min', {
...min, // 官方自带的校验规则
message: `{_field_}输入的值至少{length}位!`, // 自定义错误消息
params: ['length']
})
extend('max', {
...max, // 官方自带的校验规则
message: `{_field_}输入的值至多{length}位!`, // 自定义错误消息
params: ['length']
})
validate.forEach(item => {
extend(item.name, {validate: item.validate, params: item.params})
});
//全局注册
Vue.component("ValidationObserver", ValidationObserver)
Vue.component("ValidationProvider", ValidationProvider)
new Vue({
router,
render: h => h(App)
}).$mount('#app')
vee-validate@3项目实例
<template>
<div class="customForm">
商铺表单
<ValidationObserver tag="form" class="form" ref="form">
<ValidationProvider rules="required|min:3|max:5" name="姓名" v-slot="{ errors }">
<div class="form-item">
<label for="name">代理人姓名:
<input v-model="form.name" id="name" type="text" placeholder="请输入代理人姓名">
</label>
<span class="error">{{ errors[0] }}</span>
</div>
</ValidationProvider>
<ValidationProvider rules="required" name="类型" v-slot="{ errors }">
<div class="form-item">
<label for="selectType">
类型:
<select name="" id="selectType" placeholder="请选择类型" v-model="form.selectType" rules="reuired">
<option value="">--请选择类型--</option>
<option value="1">单区域</option>
<option value="2">多区域</option>
</select>
</label>
<span class="error">{{ errors[0] }}</span>
</div>
</ValidationProvider>
<h4>关联区域属性:</h4>
<template v-if="form.selectType == 1" >
<div v-if="oneTypeArr.length === 0">
<button class="btn" style="margin-bottom: 15px;" type="submit" @click="addChildren">添加选项</button>
</div>
<div class="flex-center" v-for="(item, i) in oneTypeArr" :key="i">
<ValidationProvider rules="required" :name="`店名${i+1}`" v-slot="{ errors }">
<div class="form-item">
<label for="storeName">
{{`店名${i+1}:`}}
<input type="text" id="storeName" v-model="item.storeName" placeholder="请输入店名">
</label>
<br>
<span>{{ errors[0] }}</span>
</div>
</ValidationProvider>
<ValidationProvider rules="required" :name="`经理${i+1}`" v-slot="{ errors }">
<div class="form-item">
<label for="manager">
{{`经理${i+1}:`}}
<input type="text" id="manager" v-model="item.manager" placeholder="请输入店铺经理">
</label>
<br>
<span>{{ errors[0] }}</span>
</div>
</ValidationProvider>
<button class="btn" type="submit" @click="addChildren">添加选项</button>
<button class="btn" style="margin-left: 15px" type="submit" @click="subChildren($event, item.index)">删除选项</button>
</div>
</template>
<template v-if="form.selectType == 2">
<div class="flex-center" v-for="(item,i) in twoTypeArr" :key="i">
<ValidationProvider rules="required" :name="`店铺名称${i+1}`" v-slot="{ errors }">
<div class="form-item">
<label for="storeName">
{{`店铺名称${i+1}:`}}
<input type="text" name="" id="storeName" v-model="item.storeName" placeholder="请输入店铺名称">
</label>
<br>
<span>{{ errors[0] }}</span>
</div>
</ValidationProvider>
<ValidationProvider rules="required" :name="`店铺标识${i+1}`" v-slot="{ errors }">
<div class="form-item">
<label for="storeCode">
{{`店铺标识${i+1}:`}}
<input type="text" id="storeCode" v-model="item.storeCode" placeholder="请输入店铺标识">
</label>
<br>
<span>{{ errors[0] }}</span>
</div>
</ValidationProvider>
</div>
</template>
<div class="form-item">
<button type="submit" @click="submit">提交</button>
</div>
</ValidationObserver>
</div>
</template>
<script>
import { nanoid } from 'nanoid'
export default {
name: 'customForm',
data () {
return {
form: {
selectType: void 0,
config:{
}
},
oneTypeArr:[{
index: 1,
storeName: '',
manager: ''
}],
twoTypeArr: [
{index: 1, storeName: '', storeCode: ''}
],
}
},
methods: {
// 添加选项
addChildren(e){
e.preventDefault();
let index = nanoid()
if(this.form.selectType === '1'){
if(this.oneTypeArr.length == 5) {return alert('最多添加5条记录')}
this.oneTypeArr.push({
index,
storeName: '',
manager: ''
})
}else if(this.form.selectType === '2'){
this.twoTypeArr.push({
index,
storeName: '',
storeCode: ''
})
}
},
// 删除选项
subChildren(e, index){
e.preventDefault()
console.log(index)
if(this.form.selectType == 1){
this.oneTypeArr = this.oneTypeArr.filter(item => item.index !== index)
}else if(this.form.selectType === 2){
this.twoTypeArr = this.twoTypeArr.filter(item => item.index !== index)
}
},
// 表单提交
submit(e){
e.preventDefault()
this.$refs.form.validate().then((status)=>{
if (!status) return false;
console.log('validate',status)
})
console.log('submit',this.$refs.form,this.form)
}
}
}
</script>
<style lang='less' scoped>
.flex-center{
display: flex;
justify-content: center;
& .form-item{
margin-right: 15px;
& span{
color: red;
}
}
}
.btn{
width: 80px;
height: 30px;
font-size: 16px;
background-color: skyblue;
border: 1px solid #000;
border-radius: 3px;
}
.customForm{
margin-top: 100px;
text-align: center;
& .form{
display: block;
height: 700px;
}
& .form-item{
height: 60px;
text-align: center;
input{
outline: none;
height: 25px;
}
select{
outline: none;
width: 170px;
height: 30px;
}
button{
width: 80px;
height: 30px;
font-size: 16px;
background-color: skyblue;
border: 1px solid #000;
border-radius: 3px;
}
& .error{
display: block;
color: red;
}
}
}
</style>
vee-validate@4版本使用
第三方包安装
npm install vee-validate@4 @vee-validate/rules
vee-validate@4项目实例
<template>
<div class="customForm">
商铺表单
<form class="form" @submit.prevent="submit">
<ValidationObserver v-slot="{ handleSubmit, errors }">
<div class="form-item">
<label for="name">代理人姓名:
<input v-model="form.name" id="name" type="text" placeholder="请输入代理人姓名" />
</label>
<span class="error">{{ errors.name ? errors.name[0] : '' }}</span>
</div>
<div class="form-item">
<label for="selectType">
类型:
<select v-model="form.selectType" id="selectType" placeholder="请选择类型">
<option value="">--请选择类型--</option>
<option value="1">单区域</option>
<option value="2">多区域</option>
</select>
</label>
<span class="error">{{ errors.selectType ? errors.selectType[0] : '' }}</span>
</div>
<h4>关联区域属性:</h4>
<template v-if="form.selectType == '1'">
<div v-if="oneTypeArr.length === 0">
<button class="btn" style="margin-bottom: 15px;" type="button" @click="addChildren">添加选项</button>
</div>
<div class="flex-center" v-for="(item, i) in oneTypeArr" :key="item.index">
<div class="form-item">
<label for="storeName">
{{`店名${i + 1}:`}}
<input v-model="item.storeName" type="text" id="storeName" placeholder="请输入店名" />
</label>
<span>{{ errors[`storeName${i}`] ? errors[`storeName${i}`][0] : '' }}</span>
</div>
<div class="form-item">
<label for="manager">
{{`经理${i + 1}:`}}
<input v-model="item.manager" type="text" id="manager" placeholder="请输入店铺经理" />
</label>
<span>{{ errors[`manager${i}`] ? errors[`manager${i}`][0] : '' }}</span>
</div>
<button class="btn" type="button" @click="addChildren">添加选项</button>
<button class="btn" style="margin-left: 15px" type="button" @click="subChildren($event, item.index)">删除选项</button>
</div>
</template>
<template v-if="form.selectType == '2'">
<div class="flex-center" v-for="(item, i) in twoTypeArr" :key="item.index">
<div class="form-item">
<label for="storeName">
{{`店铺名称${i + 1}:`}}
<input v-model="item.storeName" type="text" id="storeName" placeholder="请输入店铺名称" />
</label>
<span>{{ errors[`storeName${i}`] ? errors[`storeName${i}`][0] : '' }}</span>
</div>
<div class="form-item">
<label for="storeCode">
{{`店铺标识${i + 1}:`}}
<input v-model="item.storeCode" type="text" id="storeCode" placeholder="请输入店铺标识" />
</label>
<span>{{ errors[`storeCode${i}`] ? errors[`storeCode${i}`][0] : '' }}</span>
</div>
</div>
</template>
<div class="form-item">
<button class="btn" type="submit">提交</button>
</div>
</ValidationObserver>
</form>
</div>
</template>
<script>
import { defineRule, useForm, useField, ErrorMessage } from 'vee-validate';
import { required, min, max } from '@vee-validate/rules';
import { nanoid } from 'nanoid';
export default {
name: 'customForm',
data() {
return {
form: {
selectType: void 0,
config: {}
},
oneTypeArr: [
{
index: 1,
storeName: '',
manager: ''
}
],
twoTypeArr: [
{ index: 1, storeName: '', storeCode: '' }
]
};
},
mounted() {
defineRule('required', required);
defineRule('min', min);
defineRule('max', max);
},
methods: {
// 添加选项
addChildren(e) {
e.preventDefault();
let index = nanoid();
if (this.form.selectType === '1') {
if (this.oneTypeArr.length == 5) {
return alert('最多添加5条记录');
}
this.oneTypeArr.push({
index,
storeName: '',
manager: ''
});
} else if (this.form.selectType === '2') {
this.twoTypeArr.push({
index: nanoid(),
storeName: '',
storeCode: ''
});
}
},
// 删除选项
subChildren(e, index) {
e.preventDefault();
if (this.form.selectType == '1') {
this.oneTypeArr = this.oneTypeArr.filter(item => item.index !== index);
} else if (this.form.selectType === '2') {
this.twoTypeArr = this.twoTypeArr.filter(item => item.index !== index);
}
},
// 表单提交
submit() {
this.$refs.form.validate().then((status) => {
if (!status) return false;
console.log('validate', status);
});
console.log('submit', this.$refs.form, this.form);
}
}
};
</script>
关键修改点总结:
useForm和useField:使用useForm来管理整个表单的验证,使用useField来管理每个字段的验证。通过useField返回的errors对象获取每个字段的验证错误信息。- 表单验证:使用
@vee-validate/rules中的内置规则(如required,min,max)并通过defineRule注册自定义规则。 v-slot和errors:在vee-validate@4中,errors不再是一个普通对象,而是响应式的,直接通过useField获取每个字段的验证错误。