Skip to content

运行时校验选型:Zod vs Valibot vs ArkType

一句话概述:三大运行时校验库的 API 设计、包体积、性能、tree-shaking 能力对比——帮你做出 2026 年的最佳选择

为什么需要对比?

2026 年 TypeScript 运行时校验库的格局已经从"Zod 一家独大"变为"三足鼎立"。选择合适的库直接影响项目的包体积、开发体验和性能。


一、核心对比

基本信息

维度ZodValibotArkType
首次发布202020232023
设计理念链式 API,一体化函数式,可组合,tree-shakable类型优先,接近 TS 语法
包体积(minified)~57KB~7KB(按需引入更小)~37KB
Tree-shaking❌ 较差(class-based)✅ 优秀(function-based)⚠️ 中等
TypeScript 支持✅ 内置✅ 内置✅ 内置
生态集成最丰富(tRPC、RHF、Next.js)快速增长较新
学习曲线中(独特语法)

包体积详细对比

text
Zod v3.23:
├── 完整包:~57KB (min)
└── 无法 tree-shake(class 实例方法都会被保留)

Valibot v1.0:
├── 完整包:~7KB (min)
├── 只用 string + email:~1.5KB
└── 函数式设计,按需引入

ArkType v2.0:
├── 完整包:~37KB (min)
└── 编译器在运行时做类型解析,基础开销较大

二、API 对比

基本类型定义

typescript
// ========== Zod ==========
import { z } from 'zod';

const UserSchema = z.object({
  name: z.string().min(1),
  email: z.string().email(),
  age: z.number().int().min(0),
});

type User = z.infer<typeof UserSchema>;

// ========== Valibot ==========
import * as v from 'valibot';

const UserSchema = v.object({
  name: v.pipe(v.string(), v.minLength(1)),
  email: v.pipe(v.string(), v.email()),
  age: v.pipe(v.number(), v.integer(), v.minValue(0)),
});

type User = v.InferOutput<typeof UserSchema>;

// ========== ArkType ==========
import { type } from 'arktype';

const User = type({
  name: 'string > 0',
  email: 'string.email',
  'age?': 'integer >= 0',
});

type User = typeof User.infer;

联合类型和可辨识联合

typescript
// ========== Zod ==========
const Shape = z.discriminatedUnion('kind', [
  z.object({ kind: z.literal('circle'), radius: z.number() }),
  z.object({ kind: z.literal('rect'), width: z.number(), height: z.number() }),
]);

// ========== Valibot ==========
const Shape = v.variant('kind', [
  v.object({ kind: v.literal('circle'), radius: v.number() }),
  v.object({ kind: v.literal('rect'), width: v.number(), height: v.number() }),
]);

// ========== ArkType ==========
const Shape = type({
  kind: "'circle'",
  radius: 'number',
}).or({
  kind: "'rect'",
  width: 'number',
  height: 'number',
});

数据转换

typescript
// ========== Zod ==========
const NumberFromString = z.string()
  .transform(val => parseInt(val, 10))
  .pipe(z.number().min(0));

// ========== Valibot ==========
const NumberFromString = v.pipe(
  v.string(),
  v.transform(val => parseInt(val, 10)),
  v.number(),
  v.minValue(0),
);

// ========== ArkType ==========
const NumberFromString = type('string')
  .pipe((s) => parseInt(s, 10))
  .to('number >= 0');

错误处理

typescript
// ========== Zod ==========
const result = UserSchema.safeParse(data);
if (!result.success) {
  // result.error.issues: ZodIssue[]
  // result.error.flatten(): { formErrors, fieldErrors }
  console.log(result.error.flatten().fieldErrors);
}

// ========== Valibot ==========
const result = v.safeParse(UserSchema, data);
if (!result.success) {
  // result.issues: SchemaIssue[]
  const flat = v.flatten(result.issues);
  console.log(flat); // { nested: { name: [...], email: [...] } }
}

// ========== ArkType ==========
const result = User(data);
if (result instanceof type.errors) {
  // result.summary: string(人类可读)
  console.log(result.summary);
  // result.byPath: 按路径分组的错误
}

三、性能对比

基准测试结果(参考值)

text
简单对象校验(10 个字段):
├── ArkType:最快(预编译类型检查)
├── Valibot:次快
└── Zod:最慢(但对大多数应用足够)

复杂嵌套对象:
├── ArkType:领先优势更明显
├── Valibot:稳定
└── Zod:差距拉大

结论:
- 大多数应用场景差异可忽略(微秒级)
- 高频热路径(如请求校验中间件)才需要关注性能
- ArkType 的性能优势来自编译期优化

为什么 ArkType 更快?

ArkType 在定义 Schema 时就进行"编译"——将类型定义转换为优化后的校验函数。Zod 和 Valibot 在每次 parse 时都是解释执行。


四、生态集成对比

React Hook Form

typescript
// ========== Zod ==========
import { zodResolver } from '@hookform/resolvers/zod';
const form = useForm({ resolver: zodResolver(UserSchema) });

// ========== Valibot ==========
import { valibotResolver } from '@hookform/resolvers/valibot';
const form = useForm({ resolver: valibotResolver(UserSchema) });

// ========== ArkType ==========
// 目前无官方 resolver,需自定义

tRPC

typescript
// ========== Zod ==========(原生支持)
const router = t.router({
  createUser: t.procedure.input(UserSchema).mutation(/* ... */),
});

// ========== Valibot ==========(tRPC v11+ 支持)
import { wrap } from '@trpc/server';
const router = t.router({
  createUser: t.procedure.input(wrap(UserSchema)).mutation(/* ... */),
});

// ========== ArkType ==========
// tRPC 适配器较少

框架集成总览

框架/工具ZodValibotArkType
tRPC✅ 原生✅ 适配器⚠️ 社区
React Hook Form✅ 官方✅ 官方
Next.js Server Actions⚠️
Nuxt⚠️
Hono⚠️
Drizzle ORM
Conform

五、选型建议

选 Zod 的场景

  • 需要最完善的生态集成(tRPC、React Hook Form、Next.js 等)
  • 团队已经在使用 Zod,没有包体积压力
  • 偏好链式 API 的开发体验
  • 需要 branded types、递归类型等高级特性

选 Valibot 的场景

  • 包体积敏感(移动端、嵌入式、Serverless Edge Functions)
  • 需要最优 tree-shaking
  • 偏好函数式编程风格
  • 对 Zod API 熟悉(Valibot API 设计参考了 Zod)

选 ArkType 的场景

  • 性能敏感的高频校验场景
  • 偏好类型即 Schema 的声明式语法
  • 项目不依赖 tRPC / RHF 等需要适配器的框架
  • 愿意接受较新的库和较小的社区

决策流程图

text
包体积是首要考虑?
├── 是 → Valibot
└── 否 →
    需要 tRPC / RHF 集成?
    ├── 是 → Zod
    └── 否 →
        性能是关键因素?
        ├── 是 → ArkType
        └── 否 → Zod(生态最成熟)

六、迁移指南

Zod → Valibot

typescript
// Zod
z.string().min(1).email()
// Valibot
v.pipe(v.string(), v.minLength(1), v.email())

// Zod
z.object({ name: z.string() }).partial()
// Valibot
v.partial(v.object({ name: v.string() }))

// Zod
z.infer<typeof Schema>
// Valibot
v.InferOutput<typeof Schema>

// Zod
schema.safeParse(data)
// Valibot
v.safeParse(schema, data) // 注意:schema 是第一个参数

// Zod
schema.parse(data)
// Valibot
v.parse(schema, data)

核心差异记忆

text
Zod:    schema.method(data)    → 面向对象,方法在实例上
Valibot: method(schema, data)  → 函数式,schema 作为参数传入
ArkType: type("syntax")       → 类型字符串,接近 TS 语法

面试高频题

1. Zod 和 Valibot 的核心区别是什么?

答案:最核心的区别是 API 设计范式——Zod 是面向对象的链式 API(z.string().min(1)),所有方法挂在 class 实例上,无法 tree-shake;Valibot 是函数式 API(v.pipe(v.string(), v.minLength(1))),每个校验器是独立函数,支持按需引入。这导致 Valibot 的包体积只有 Zod 的约 1/8。

2. 什么场景下包体积差异真的重要?

答案:Serverless Edge Functions(Cloudflare Workers 有 1MB 限制)、移动端 PWA(弱网环境首屏加载)、以及被大量项目依赖的基础库。普通 Web 应用中 50KB 的差异通常不是瓶颈,此时生态成熟度(Zod)或性能(ArkType)可能是更重要的考量。

3. ArkType 的类型字符串语法有什么优缺点?

答案:优点是非常接近 TypeScript 原生语法,学习成本低且代码更简洁(type({ age: 'number >= 0' }))。缺点是字符串中的类型表达式没有 IDE 补全和即时类型检查,出错时错误信息不如 Zod/Valibot 直观,且社区生态和框架集成还不够成熟。

4. 这三个库能混用吗?

答案:技术上可以在同一项目中使用多个校验库,但不推荐。它们的类型推断方式不同(z.infer vs v.InferOutput vs typeof schema.infer),混用会增加心智负担和包体积。如果要迁移,建议逐模块替换而非混用。