React 用 Zod 统一前后端数据校验:把规则抽成可复用的 schema

test12026-10-131 次阅读

为什么要在前端额外做 schema 校验

表单库负责把输入收集成值,但值的"形状"和"规则"往往散落在各处。把校验抽成一个 Zod schema,既能给 react-hook-form 用,也能在拿到接口响应时再校验一遍,前后端共用同一套规则描述,类型与规则不会出现两份互相打架的写法。

定义可复用的 schema

import { z } from "zod";

export const loginSchema = z.object({
  email: z.string().email("邮箱格式不正确"),
  password: z.string().min(8, "密码至少 8 位"),
});

// 从 schema 反推类型,运行期规则即编译期类型
export type LoginInput = z.infer<typeof loginSchema>;

接入 react-hook-form

用 @hookform/resolvers/zod 把 schema 接到 resolver,提交时自动跑校验,错误直接落到 formState.errors。

import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";

function LoginForm() {
  const { register, handleSubmit, formState: { errors } } =
    useForm<LoginInput>({ resolver: zodResolver(loginSchema) });

  const onSubmit = (data: LoginInput) => {
    // data 在此已被 Zod 收窄为合法类型
    console.log(data.email);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register("email")} />
      {errors.email && <span>{errors.email.message}</span>}
      <input type="password" {...register("password")} />
      {errors.password && <span>{errors.password.message}</span>}
      <button type="submit">登录</button>
    </form>
  );
}

校验接口响应,别相信后端

拿到 fetch 结果后用 schema 再校验一次,类型不对直接走兜底,避免脏数据进入 UI 引发更隐蔽的崩溃。

const res = await fetch("/api/user").then((r) => r.json());

const userSchema = loginSchema.extend({ id: z.number() });
const parsed = userSchema.safeParse(res);

if (!parsed.success) {
  // 上报错误或提示数据异常
  console.error(parsed.error.flatten());
  return fallbackUser;
}
return parsed.data;

小结

  • schema 是单一事实来源,表单校验和响应校验复用同一份描述。
  • z.infer 把运行期规则变成编译期类型,类型与校验不再分家。
  • safeParse 不抛异常,返回 success 标记,适合处理不可信的外部数据。
T

test1

文章作者

为什么要在前端额外做 schema 校验 表单库负责把输入收集成值,但值的"形状"和"规则"往往散落在各处。把校验抽成...

分类
技术
发布时间
2026-10-13
字数
约 1738 字
阅读
1 次