前端表单和 Node 接口都要拦一遍「进来的数据长什么样」。TypeScript 类型在编译期就没了,运行时仍得有一份可执行的 schema。不少项目已经用过 Zod 一类库。换库之前,更想先搞清新选项自己怎么定位,而不是听口号「必须换谁」。Valibot 官方 Introduction 把自己写成模块化、类型安全的 schema 库:无依赖,能在常见 JavaScript 环境跑,对象从服务端入参到配置文件都能验。
官方强调的「轻」指什么
页面 Highlights 写得很清楚:静态类型推断、体积从不到 700 字节起跳、能验从字符串到复杂对象、带一批变换与校验动作、源码无依赖、API 偏小而可读。和「一个大对象上挂很多方法」不同,Valibot 的设计是大量小而独立的函数,每个只干一件事。打包器可以按 import 做树摇,没用到的校验器进不了生产包。官方对比里写到,相对 Zod 一类单体式设计,体积有机会少掉很大一截(文档用了「最多约 95%」这种量级表述,回该页核对即可,这里不另编基准测试)。
这不等于「Zod 立刻作废」。官方甚至单独准备了迁移说明和迁移指南,给的是选择与迁移路径,不是站队宣言。团队若 Zod 已经铺开、包体积也还过得去,完全可以继续用。新项目或对 bundle 特别敏感的路径,再评估 Valibot 更合适。
最小心智:schema 能跑,不只是类型注释
官方把 schema 比成 TypeScript 类型定义,但点出真正差别:TS 类型不会在运行时执行。schema 既能推断类型,又能在运行时保证未知数据的形状。入门示例是登录对象:
import * as v from 'valibot';
const LoginSchema = v.object({
email: v.pipe(v.string(), v.email()),
password: v.pipe(v.string(), v.minLength(8)),
});
type LoginData = v.InferOutput<typeof LoginSchema>;
const output = v.parse(LoginSchema, {
email: 'jane@example.com',
password: '12345678',
});
v.object 描述结构,v.pipe 把「是字符串」和「满足邮箱 / 最小长度」串起来。InferOutput 从 schema 推出 TS 类型,避免手写一份类型再维护另一份校验。parse 在失败时抛错。同页还提到非抛错的 safeParse,以及当类型守卫用的 is,细节链到文档其他章节。空邮箱和过短密码的示例在官方页里会直接抛错,用来演示「未知输入必须过 schema」这件事。
模块化带来的写法差异
从 Zod 过来的人,容易怀念「一个 schema 对象上链式点方法」。Valibot 更像把基础类型和动作拆开再组合:先 v.string(),再经 v.pipe 接 v.email() / v.minLength()。文档说这种拆法方便外部代码扩展,也方便单测每个小函数。迁移时按官方迁移文对照 API,别凭印象把方法名一对一硬翻。
树摇的前提是你的打包器真的按 import 分析。若项目里 import * as v 之后又动态拼出一堆校验器名字,树摇收益会变差。这是打包常识,官方入门页没展开,但和「模块化设计」是同一方向上的取舍。
什么时候适合试一把
bundle 敏感的前端、边缘函数、希望按需引入校验器的库作者,模块化 API 收益更直观。服务端若本来就不在意多几十 KB,优先级会低一些,但统一前后端同一套 schema 仍然省事。配置文件、环境变量解析也可以走同一套对象 schema,减少「表单一套、接口一套、配置又手写」的分叉。
上手时注意:import 用文档示例的 import * as v from 'valibot' 命名空间风格。校验失败信息、自定义消息、异步校验等进阶能力看文档目录,入门页只保证「先能描述对象并 parse」。许可证是 MIT,反馈走 GitHub issue,这些官方页末尾有写,不影响选型判断。
你们现在是卡在包体积,还是卡在「类型和运行时校验各写一份」的重复劳动?