Vue 3 表单验证进阶:用 vee-validate + yup 接管复杂联动校验
test12026-10-027 次阅读
手写校验的代价
表单一多,手动 v-model 加 if 判断既啰嗦又难维护,尤其是"两次密码一致""选了 A 才必填 B"这类联动规则。vee-validate 负责绑定与错误展示,yup 负责描述校验规则,两者配合能把校验逻辑从模板里彻底抽离。
第一步:定义校验 schema
用 yup 描述字段与联动规则。.oneOf([yup.ref("password")]) 实现确认密码一致,.when() 实现条件必填。
import * as yup from "yup";
const schema = yup.object({
username: yup.string().min(3, "用户名至少 3 个字符").required("请输入用户名"),
email: yup.string().email("邮箱格式不正确").required("请输入邮箱"),
password: yup.string().min(8, "密码至少 8 位").required("请输入密码"),
confirm: yup
.string()
.required("请再次输入密码")
.oneOf([yup.ref("password")], "两次密码不一致"),
inviteCode: yup.string().when("isTeam", {
is: true,
then: (s) => s.required("团队注册必须填写邀请码"),
otherwise: (s) => s.notRequired(),
}),
});
第二步:在组件里接入
useForm 接收 schema 与提交回调,useField 把每个字段绑定到输入框,错误信息通过 errorMessage 直接渲染。
<script setup lang="ts">
import { useForm, useField } from "vee-validate";
const { handleSubmit, values } = useForm({
validationSchema: schema,
initialValues: { isTeam: false },
});
const { value: username, errorMessage: usernameErr } = useField("username");
const { value: email, errorMessage: emailErr } = useField("email");
const { value: password, errorMessage: passwordErr } = useField("password");
const { value: confirm, errorMessage: confirmErr } = useField("confirm");
const { value: inviteCode, errorMessage: inviteErr } = useField("inviteCode");
const onSubmit = handleSubmit((form) => {
console.log("提交通过:", form);
});
</script>
<template>
<form @submit="onSubmit">
<input v-model="username" placeholder="用户名" />
<p v-if="usernameErr">{{ usernameErr }}</p>
<input v-model="email" placeholder="邮箱" />
<p v-if="emailErr">{{ emailErr }}</p>
<input v-model="password" type="password" placeholder="密码" />
<p v-if="passwordErr">{{ passwordErr }}</p>
<input v-model="confirm" type="password" placeholder="确认密码" />
<p v-if="confirmErr">{{ confirmErr }}</p>
<label>
<input type="checkbox" v-model="values.isTeam" /> 团队注册
</label>
<input v-model="inviteCode" placeholder="邀请码(团队必填)" />
<p v-if="inviteErr">{{ inviteErr }}</p>
<button type="submit">注册</button>
</form>
</template>
第三步:异步查重
需要校验用户名是否已被占用时,给字段加一个异步校验函数即可,yup 的 .test() 也支持返回 Promise。
const schema = yup.object({
username: yup
.string()
.required("请输入用户名")
.test("unique", "用户名已被占用", async (val) => {
if (!val) return true;
const res = await fetch(`/api/check-username?u=${val}`);
const { ok } = await res.json();
return ok;
}),
});
工程要点
- 校验规则全部集中在 schema 里,组件模板只剩绑定与展示,逻辑可单测。
- 联动校验优先用
.when(),比在提交函数里写一堆if更不容易漏分支。 - 异步校验要加防抖与竞态处理,否则快速输入会拿到过期结果;可结合
abortController取消旧请求。 - 多表单复用同一 schema 时,把它抽到独立的
validations/*.ts文件,按场景组合。
T
test1
文章作者
手写校验的代价 表单一多,手动 v-model 加 if 判断既啰嗦又难维护,尤其是"两次密码一致""选了 A 才必填...
- 分类
- 技术
- 发布时间
- 2026-10-02
- 字数
- 约 2872 字
- 阅读
- 7 次
