AOT 编译
Zod 可以提前将模式编译成扁平、无循环的验证器,其运行速度比标准解析器快数倍。结果和错误完全一致。
像使用 Player 一样使用它:
像 CompiledPlayer 这样的已编译模式和其他任何 Zod 模式一样。已编译模式没有特殊规则。
- 相同的方法:
.parse()、.safeParse()、.extend()、.optional()等 - 相同的推断输入和输出类型
- 相同的问题和错误消息
Zod 的完整测试套件会运行两次——一次正常运行,另一次则全局启用自动编译——以确保完全一致。
对象和元组等容器受益最大,因为编译会将运行时逐键遍历展开为扁平、无循环的验证逻辑,从而可以由 JS 引擎进行优化。
有两种启用方式。
z.compile()
编译单个模式并返回一个编译后的副本。原模式不会改变。
派生出新模式的方法(.refine()、.extend()、.optional()、.meta()……)会返回未编译的模式。请编译最终模式,而不是中间模式:
import "zod/compile"
全局启用编译。在此导入之后构造的每个模式,都会在首次解析时自动编译。
编译是惰性的,因此只有实际进行解析的模式才会被编译。
它也可以作为 Node.js CLI 标志使用,这样可以确保它在任何模块定义模式之前运行:
或者在 bunfig.toml 或 nub.jsonc 中设置 preload。
此导入方式用于应用程序,而不是库。
工作原理
在底层,z.compile() 会遍历整个模式一次,并生成一段高度优化的扁平、无循环 JavaScript 代码,其验证输入的速度远快于标准运行时验证器。这段代码可以通过 new Function() 执行(实际上是功能更强大的 eval),作为快速路径验证器使用。模式会利用它来“快速检查”有效性,在验证失败时回退到常规运行时逻辑,以提供详细的错误信息。
请看这个简单的 Point schema:
这是为它生成的代码片段:
对于绝大多数输入,生成的函数会使用 JavaScript 能够表达的最快逻辑来验证数据:直接执行 typeof 检查和属性读取,中间没有解释器介入。当它无法处理某个输入时,Zod 会回退到标准解析器。
这是 Zod 为上面的 Player schema 生成的函数:
借助 new Function() 的能力,这一切会在运行时于进程内完成。无需集成到你的构建系统中。
INVALID 符号,以表示解析应回退到未编译的解析器。这在结构上避免了编译版本与未编译版本之间错误报告的细微偏差。对于无效输入,回退逻辑会运行未编译的模式,因此错误来自未编译模式。由此带来两个结果:
- 无效输入需要同时经过快速路径和回退逻辑,因此编译不会加快失败情况。
- 对于有效输入,细化和转换只会运行一次;对于无效输入,最多运行两次。
不支持的模式
有些功能无法编译,或者从编译中无法受益。在这些情况下,z.compile() 会退出编译并原样返回原模式:
async细化、转换和检查z.xor()- 递归模式
z.coerce.*- 带有自定义
when的检查 - 使用回调作为参数的
.catch()(带常量的.catch(value)会正常编译)
在对象、数组、元组、记录或交集中,不支持的子模式会使用标准解析器运行,而外围结构仍会保持编译状态。包含不支持成员的联合、.catch() 回调,或子树中任意位置存在异步逻辑,都会使整个模式回退。
编码(z.encode()、“backward”方向的 codec)和异步解析始终使用标准解析器。
传入 strict 可以让其抛出错误而不是回退——例如,用于确认热路径上的模式确实完成了编译:
对于异步模式会抛出 ZodCompileAsyncError,其他所有情况会抛出 ZodCompileUnsupportedError。这两种错误都只会在 strict 下抛出。
内容安全策略
编译使用 new Function,而它在 CSP/不允许使用 eval 的环境中不可用。设置 jitless 后,全局模式会停用:
直接调用 z.compile() 是显式选择启用,因此无论 jitless 的设置如何,它都会尝试生成代码。如果环境拒绝 new Function,该模式会像其他拒绝编译的情况一样,以未编译状态返回。
包体积
编译器包含大量代码,通过 z.compile() 或 "zod/compile" 调用它意味着编译器会被包含在你的包中。它会增加约 7 KB 的 gzip 压缩体积(压缩后 28 KB)。从未调用 z.compile() 或导入 zod/compile 的包无需承担任何开销;打包时它会被完全 tree-shaking。
| 包(四键对象模式) | 不含编译器 | 含编译器 |
|---|---|---|
| Zod | 24.1 KB | 31.1 KB |
| Zod Mini | 4.6 KB | 13.2 KB |
基准测试
收益会随着模式复杂度增加而提升。这里的每个模式都是在紧密循环中单独测量的——这是标准解析器的最佳情况——因此其倍数低于页面顶部图表中的结果(基准测试)。
| 模式 | 加速比 |
|---|---|
| 对象,5 个键 | 1.8 倍 |
| 对象,10 个键 | 2.2 倍 |
| 对象,20 个键 | 5.0 倍 |
| 对象,50 个键 | 10.2 倍 |
| 元组,1 个项 | 2.2 倍 |
| 元组,3 个项 | 2.5 倍 |
| 元组,5 个项 | 3.0 倍 |
| 元组,10 个项 | 3.7 倍 |
基于 Moltar 基准测试 fixture 的结果,将编译和未编译的 Zod 与其他库进行比较。parseSafe 类别会返回一个剥离未知键的新对象。
assertLoose 类别返回布尔值并允许未知键。Zod 会通过 z.validate() 运行它。

