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 次