现在我们越来越多的在 UI 组件库里找到 Zod 的存在,Zod 是什么?我们为什么要用 Zod 呢?
根据其官网说法,zod 利用静态类型推断进行 TypeScript 优先模式验证。在 shadcn、nuxt ui 等组件库中,其主要是用来进行表单的校验;在一些 js 全栈项目里,它用来规定项目的数据结构。
我们就来简单的看一下它是怎么使用的。
这里我使用 pnpm 作为包管理工具,并使用 vue 项目举例。
安装
pnpm add zod
一些常用的内容
一些常用的字符串的验证
// 验证
z.string().max(5); // 最大长度
z.string().min(5); // 最小长度
z.string().length(5); // 字符长度
z.string().email();
z.string().url();
z.string().emoji();
z.string().uuid();
z.string().regex(regex); // 使用正则校验
z.string().includes(string);
z.string().startsWith(string);
z.string().endsWith(string);
z.string().datetime(); // ISO 8601;默认值为无 UTC 偏移,选项见下文
z.string().ip(); // 默认为 IPv4 和 IPv6,选项见下文
// 转变
z.string().trim(); // 减除空白
z.string().toLowerCase(); // 小写化
z.string().toUpperCase(); // 大写化
创建字符串模式时,你可以自定义一些常见的错误信息
const name = z.string({
required_error: "Name is required",
invalid_type_error: "Name must be a string",
});
自定义错误消息
使用验证方法时,你可以传递一个附加参数,以提供自定义错误信息
z.string().min(5, { message: "Must be 5 or more characters long" });
z.string().max(5, { message: "Must be 5 or fewer characters long" });
z.string().length(5, { message: "Must be exactly 5 characters long" });
z.string().email({ message: "Invalid email address" });
z.string().url({ message: "Invalid url" });
z.string().emoji({ message: "Contains non-emoji characters" });
z.string().uuid({ message: "Invalid UUID" });
z.string().includes("tuna", { message: "Must include tuna" });
z.string().startsWith("https://", { message: "Must provide secure URL" });
z.string().endsWith(".com", { message: "Only .com domains allowed" });
z.string().datetime({ message: "Invalid datetime string! Must be UTC." });
z.string().ip({ message: "Invalid IP address" });
特定的数字验证
z.number().gt(5); // 最小为 5
z.number().gte(5); // alias .min(5)
z.number().lt(5); // 最大为 5
z.number().lte(5); // alias .max(5)
z.number().int(); // 必须是一个整数
z.number().positive(); // > 0
z.number().nonnegative(); // >= 0
z.number().negative(); // < 0
z.number().nonpositive(); // <= 0
z.number().multipleOf(5); // Evenly divisible by 5. Alias .step(5)
z.number().finite(); // value must be finite, not Infinity or -Infinity
z.number().safe(); // value must be between Number.MIN_SAFE_INTEGER and Number.MAX_SAFE_INTEGE
和字符串一样,也可以传递第二个参数来自定义错误消息
对象类型
const userSchema = z.object({
username: z.string(),
password: z.string()
.min(5, { message: '密码最小为 5 位' })
.max(10, { message: '密码最大为 10 位' }),
})
// 可以使用 z.infer来提取 schema 的 ts 类型
type User = z.infer<typeof userSchema>
// 相当于 type User = {username: string, password: string}
受 TypeScript 内置的Pick和Omit工具类型的启发,所有 Zod 对象模式都有.pick和 .omit方法,可以返回一个修改后的版本
要想只保留某些 Key,使用 .pick .
const passwordSchema = userSchema.pick({ password: true });
type Password = z.infer<typeof passwordSchema>; // => type Password = {password: string}
要删除某些 Key,请使用 .omit .
const passwordSchema = userSchema.pick({ username: true });
type Password = z.infer<typeof passwordSchema>; // => type Password = {password: string}
optional
你可以用z.optional()使任何模式成为可选:
const userSchema = z.object({
username: z.string(),
password: z.string(),
age: z.number().optional()
})
可以使用 coerce 对原始类型进行强制转换
// 对传入的 id 做校验并强制转换为数字类型
// /detail/:id
const paramsSchema = z.object({
id: z.coerce.number()
})
const { id } = paramsSchema.parse(router)
还有很多其他的校验内容,可以去 zod 的官网或 github 仓库去查看
Zod 官网地址zod.dev
使用技巧
1. 在 vue 项目中校验 router 传递的参数
这里使用safeParse
进行校验,校验失败后不会抛出错误,我们可以根据它返回的 success 是否为 true 来判断校验是否通过。并可以在 error 中得到校验失败的信息进行相应的处理。
<script lang="ts" setup>
import { useRoute } from "vue-router"
import { z } from "zod"
const route = useRoute()
const paramsSchema = z.object({
id: z.coerce.number({ message: "id必须是一个数字" }),
})
const { success, data: params, error } = paramsSchema.safeParse(route.params)
</script>
<template>
<div>detail {{ params?.id }}</div>
<pre v-if="!success">{{ error }}</pre>
</template>
同理,我们也可以校验它的 query 类型,此处便不再重复。
2. 传递数据时只传递指定的字段并对其进行校验
向接口传递数据时,我们有时会有一些多余的参数,此时我们可以构建一个 schema 来对传递的参数做校验,校验后获得的值为 schema 中规定的数据结构,可以帮助我们校验数据类型并且去除 schema 中未定义的多余参数
import { z } from "zod"
const userSchema = z.object({
username: z.string(),
age: z.number(),
})
function submit() {
// 这里模拟从别处拿到的值
const user = {
username: "月空人",
password: '123456',
age: 26,
id: 1,
}
console.log("submit", userSchema.parse(user))
// 这里打印的值是 submit {username: '月空人', age: 26}
}
submit()
同理,如果我们是一个 node 的接口,我们也可以使用这个技巧来向前端传递指定 schema 类型的数据。
评论区
评论加载中...