💎 Zod 4 现已稳定发布! 阅读公告。
Zod logo

Zod Mini

Edit this page

注意 — Zod Mini 的文档通过带有标签页的代码块与常规 Zod 文档穿插展示。本页旨在讲解 Zod Mini 存在的原因,何时使用它,以及它与常规 Zod 的一些关键区别。

Zod Mini 变体在 Zod 4 发布时推出。要尝试它:

npm install zod@^4.0.0

导入方法:

import * as z from "zod/mini";

Zod Mini 实现了与 zod 完全相同的功能,但采用了函数式支持 tree-shaking 的 API。如果你熟悉 zod,这意味着你通常会使用函数代替方法。

// 常规 Zod
const mySchema = z.string().optional().nullable();
 
// Zod Mini
const mySchema = z.nullable(z.optional(z.string()));

Tree-shaking

Tree-shaking 是现代打包工具用来从最终代码包中移除未使用代码的技术。它也称为死代码消除

在常规 Zod 中,模式提供了多种方便的方法来执行常见操作(如字符串模式上的 .min())。打包工具通常无法从你的包中移除(“tree-shake”)未使用的方法实现,但能移除未使用的顶层函数。因此,Zod Mini 的 API 更倾向于使用函数而非方法。

// 常规 Zod
z.string().min(5).max(10).trim()
 
// Zod Mini
z.string().check(z.minLength(5), z.maxLength(10), z.trim());

大致了解包体积减少情况,考虑这个简单脚本:

z.boolean().parse(true)

打包时,使用 Zod 和 Zod Mini 得到的包大小如下。Zod Mini 减少了 64% 的体积。

包大小(gzip)
Zod Mini2.12kb
Zod5.91kb

使用稍复杂一点涉及对象类型的模式:

const schema = z.object({ a: z.string(), b: z.number(), c: z.boolean() });
 
schema.parse({
  a: "asdf",
  b: 123,
  c: true,
});
包大小(gzip)
Zod Mini4.0kb
Zod13.1kb

这能让你感受到相关的包大小。请仔细查看这些数据并自行测试,以判断使用 Zod Mini 是否适合你的场景。

何时(不)使用 Zod Mini

一般来说,除非你对包体积有异常严格的限制,否则建议使用常规 Zod。许多开发者过度高估了包体积对应用性能的重要性。实际上,Zod 规模的包体积(通常 5-10kb)只有在针对偏远或发展中地区存在慢速移动网络连接的用户群体时,才会成为优化前端包的显著考量。

接下来讲几个考虑点:

开发体验(DX)

Zod Mini 的 API 更啰嗦且不易发现。Zod 的方法 API 在 Intellisense 中更易发现和自动补全。无法快速用链式调用构建模式。(作为 Zod 的作者,我用了很多时间设计 Zod Mini API 以提升易用性,但我仍更喜欢常规 Zod API。)

后端开发

如果您在后端使用 Zod,Zod 的包大小并没有意义。这在像 Lambda 这样的资源受限环境中也是如此。这篇文章 对不同大小的包进行了冷启动时间的基准测试。以下是结果的一个子集:

包大小Lambda 冷启动时间
1kb171ms
17kb(非 Mini Zod 的 gzip 大小)171.6ms(插值估算)
128kb176ms
256kb182ms
512kb279ms
1mb557ms

最小包的冷启动时间为 171ms。接下来测试的 128kb 包只增加了 5ms。常规 Zod gzip 大小约为 17kb,对应冷启动时间增加大约 0.6ms

网络速度

通常,往返服务器的时间(100-200ms)远大于多下载 10kb 的时间。只有在慢速 3G 连接(低于 1Mbps)时,额外 10kb 的下载才会显得重要。如果你不专门针对偏远或发展中地区用户优化,更应该把时间花在其他性能优化上。

ZodMiniType

所有 Zod Mini 的模式都继承自 z.ZodMiniType 基类,而该类又继承自 zod/v4/core 中的 z.core.$ZodType。虽然它实现的方法比 zod 中的 ZodType 少很多,但依然保留了一些非常实用的方法。

.parse

这是最显而易见的。所有 Zod Mini 模式都实现了与 zod 相同的解析方法。

import * as z from "zod/mini"
 
const mySchema = z.string();
 
mySchema.parse('asdf')
await mySchema.parseAsync('asdf')
mySchema.safeParse('asdf')
await mySchema.safeParseAsync('asdf')

.check()

在 regular Zod 中,模式子类有专门的方法用来执行常见验证:

import * as z from "zod";
 
z.string()
  .min(5)
  .max(10)
  .refine(val => val.includes("@"))
  .trim()

但在 Zod Mini 中没有实现此类方法。取而代之的是通过向模式调用 .check() 传入检查规则:

import * as z from "zod/mini"
 
z.string().check(
  z.minLength(5), 
  z.maxLength(10),
  z.refine(val => val.includes("@")),
  z.trim()
);

以下是可用的检查。部分检查只适用于特定类型(例如字符串或数字)。所有 API 是类型安全的;TypeScript 不允许向模式添加不支持的检查。

z.lt(value);
z.lte(value); // 别名:z.maximum()
z.gt(value);
z.gte(value); // 别名:z.minimum()
z.positive();
z.negative();
z.nonpositive();
z.nonnegative();
z.multipleOf(value);
z.maxSize(value);
z.minSize(value);
z.size(value);
z.maxLength(value);
z.minLength(value);
z.length(value);
z.regex(regex);
z.lowercase();
z.uppercase();
z.includes(value);
z.startsWith(value);
z.endsWith(value);
z.property(key, schema);
z.mime(value);
 
// 自定义检查
z.refine()
z.check()   // 替代 .superRefine()
 
// 变换(它们不会改变推断的类型)
z.overwrite(value => newValue);
z.normalize();
z.trim();
z.toLowerCase();
z.toUpperCase();
 
// metadata (registers schema in z.globalRegistry)
z.meta({ title: "...", description: "..." });
z.describe("...");

.register()

用于在 注册表 中注册一个模式。

const myReg = z.registry<{title: string}>();
 
z.string().register(myReg, { title: "我的酷炫字符串模式" });

.brand()

用于品牌化一个模式。详情参见 品牌类型 文档。

import * as z from "zod/mini"
 
const USD = z.string().brand("USD");

.clone(def)

用提供的 def 返回当前模式的完全克隆。

const mySchema = z.string()
 
mySchema.clone(mySchema._zod.def);

无默认语言环境

虽然 regular Zod 默认自动加载英语 (en) 语言环境,但 Zod Mini 不会加载。这样减少了在不需要错误信息、本地化成非英语语言或其他定制情况下的包体积。

这意味着默认情况下,所有错误的 message 属性都会简单显示为 "Invalid input"。要加载英语语言环境:

import * as z from "zod/mini"
 
z.config(z.locales.en());

有关本地化的更多信息,请参考 语言环境 文档。

On this page