原生的 v-model 结合 computed 或 watch 可以做简单校验,但当表单字段变多、校验规则变复杂(如联动校验、异步校验)时,手写校验逻辑很快就会让组件变得臃肿难读。这时就需要一个专项表单校验库来把规则声明化,让模板和逻辑都保持干净。
VeeValidate 是 Vue 生态中最成熟的表单校验方案之一。它的核心思路是:在模板上用组件和指令声明校验规则,由 VeeValidate 自动追踪值变化、执行校验、收集错误信息,开发者只用关注业务逻辑,不用再手写一堆 if/else 和 ref 来维护错误状态。
安装与快速上手
npm install vee-validate
VeeValidate 暴露了一套组合式函数和组件,最常用的入口是在 main.js 中进行全局注册:
import { createApp } from 'vue'
import { Field, Form, ErrorMessage, defineRule, configure } from 'vee-validate'
import { required, email } from '@vee-validate/rules' // 按需导入规则
import { localize } from '@vee-validate/i18n'
import zhTW from '@vee-validate/i18n/dist/locale/zh_TW.json'
// 定义全局规则
defineRule('required', required)
defineRule('email', email)
// 配置全局行为
configure({
generateMessage: localize('zh_TW') // 错误信息中文化
})
const app = createApp(App)
app.component('Field', Field)
app.component('Form', Form)
app.component('ErrorMessage', ErrorMessage)
app.mount('#app')
如果不想全局注册,也可以在单个组件里按需引入。
核心用法:用组件替代原生标签
原本你可能会这样写校验:
<input v-model="email" @blur="validateEmail" />
<span v-if="emailError">{{ emailError }}</span>
VeeValidate 则让你用 <Field> 组件替换原生输入框,并把校验规则写在 rules 属性上:
<Form @submit="handleSubmit">
<label>邮箱</label>
<Field name="email" type="email" rules="required|email" />
<ErrorMessage name="email" />
<button type="submit">提交</button>
</Form>
发生了什么:
<Field>内部渲染一个<input>,并自动处理v-model和校验触发(默认为blur或change)。rules="required|email"声明了两个校验规则,用管道符|分隔,依次执行。<ErrorMessage name="email">会自动显示当前字段对应的第一项错误信息,无需手动控制显隐。<Form>包裹整个表单,提供@submit事件,该事件只在所有字段校验通过后才触发。
规则怎么写:内置规则与链式组合
VeeValidate 提供大量开箱即用的规则:
| 规则 | 说明 | 示例 |
|------|------|------|
| required | 必填 | rules="required" |
| email | 邮箱格式 | rules="email" |
| min:6 | 最小长度/数值 | rules="min:6" |
| max | 最大长度/数值 | rules="max:20" |
| confirmed | 确认字段(如密码确认) | rules="confirmed:@password" |
| regex | 正则匹配 | rules="regex:^[a-zA-Z]+$" |
也可以给一个字段同时指定多条规则:
<Field name="password" rules="required|min:8|max:20" />
自定义规则与异步校验
业务里难免有特殊逻辑,比如“用户名不能已存在”。VeeValidate 的自定义规则很简单——一个返回布尔或错误信息的函数:
import { defineRule } from 'vee-validate'
defineRule('uniqueUsername', async (value) => {
if (!value) return true
const exists = await api.checkUsername(value) // 模拟异步接口
if (exists) {
return '该用户名已被注册'
}
return true
})
然后在 rules 中使用 uniqueUsername 即可。异步校验会自动等待 Promise resolve,校验期间字段会进入 pending 状态,UI 上可以展示加载态。
错误信息与本地化
@vee-validate/i18n 提供了 40+ 语言包,简单引入就能把 required 的英文提示“The XX field is required”换成中文“XX 字段是必填的”。如果某个字段需要个性化文案,可以直接在规则中传参:
<Field name="title" :rules="{ required: true, max: { length: 10, message: '标题最多10个字' } }" />
或者用 ErrorMessage 的作用域插槽做自定义渲染:
<ErrorMessage name="email" v-slot="{ errors }">
<div v-for="error in errors" :key="error" class="error-tip">{{ error }}</div>
</ErrorMessage>
手动校验与表单重置
有些场景需要显式触发表单校验,比如点击“保存草稿”时不校验,点击“提交”时完整校验。你可以通过 useForm 组合式函数拿到校验方法:
<script setup>
import { useForm } from 'vee-validate'
const { handleSubmit, resetForm, errors, values } = useForm()
const submit = handleSubmit((formValues) => {
console.log('校验通过,表单数据:', formValues)
api.save(formValues)
})
</script>
resetForm() 可以清空所有字段的输入值和错误状态,非常适合关闭弹窗时调用。errors 对象让你可以自由判断哪个字段有错误,比如通过 errors.email 获取邮箱的详细错误。
什么时候用 VeeValidate
- 你的表单比较复杂,包含动态添加字段、嵌套对象数组、字段联动禁用等逻辑。
- 你需要统一且可维护的校验规则管理,而不是把规则散落在一堆内联函数中。
- 团队希望减少模板中关于校验状态的重复代码,把
v-if=“emailError”这类逻辑交给库处理。 - 项目不要求必须强绑定某一套 UI 组件库的校验风格,或者你用的是非 Element/Ant Design 体系的 UI 框架(如 Tailwind、Naive UI 的 core 包)。
如果你的项目已经深度使用 Element Plus 或 Ant Design Vue,并且所有表单都直接套用它们的 <el-form> 组件和内置校验规则,那 VeeValidate 可能不是必需品(16.3 节会讲)。但若你需要一份跨组件库统一、更灵活的方案,或者你在开发一个需要完全自定义 UI 的项目,VeeValidate 就是那个“让表单校验回归声明式”的好帮手。