再见 Zod,你好 Valibot!
今天是坚持日更的第142天,如果文章对您有帮助,点击关注、点赞、在看支持我
随着 TypeScript 在开发人员中的普及,对强大的验证库的需求变得越来越重要。验证库有助于确保数据的正确性、一致性和使用安全性。Zod、Joi、Yup、Valibot 是 TypeScript 最受欢迎的几个验证库。我们来看看这几个库的 NPM 包下载量:
从上图可以看出,Joi 出现最早,下载量最多;Zod 在 2023 年受到开发者追捧,迎来了指数级增长,超越 Yup;Valibot 发布 6 个月,Valibot 的周下载量已突破 50,000 次,Valibot 很有潜力成为 Zod/Yup 的替代品。
什么是验证库?
在深入了解每个库的具体细节之前,我们先来定义一下验证库的含义。验证库是一种在代码中使用数据之前帮助确保数据有效的工具。在 TypeScript 中,这通常包括检查对象的形状和类型,以及验证输入参数和返回值。一个好的验证库应该易于使用、灵活且可扩展。
Zod 介绍
Zod 是一个相对较新的验证库,在 TypeScript 社区广受欢迎。它以简单的语法、强大的类型推断和性能而闻名。
Zod 旨在尽可能地方便开发人员使用。目标是消除重复的类型声明。使用 Zod,您只需声明一次验证器,Zod 就会自动推断出 TypeScript 的静态类型。将简单类型组合成复杂的数据结构也很容易。
亮点:
零依赖。 可在 Node.js 和所有现代浏览器中运行。 语法简单:语法简单直观,易于使用,它使用可链式 API,读起来就像自然语言,让人很容易理解正在发生什么。 稳健的类型推断:可以根据验证模式推断出复杂的类型,从而轻松处理复杂的数据结构。它还支持部分属性和可选属性,这在处理应用程序接口时特别有用。 性能:在设计时充分考虑了性能。它重量轻、捆绑包小,压缩后仅有 8kb,并针对快速验证进行了优化。 自定义错误信息:允许您自定义错误信息,从而更容易向用户提供有用的反馈.
Zod 的主要优点之一是简单。它易于上手,语法也易于理解。不过,它可能不是更复杂场景的最佳选择,因为它缺乏流行校验库(如 Joi、Yup等)的一些更高级功能。
Valibot 介绍
Valibot 受 Zod 启发,也使用模式轻松验证数据,但 API 更适合利用 tree-shaking,还能更好地压缩,相比 Zod 更小。无论是服务器上的传入数据,还是表单,甚至是配置文件。没有任何依赖性,可以在任何 JavaScript 环境中运行。
亮点:
零依赖 可在 Node.js 和所有现代浏览器中运行。 语法简单:它不使用像 Zod 的链式 API 调用,而是采用更小的独立的函数的 API 设计。 稳健的类型推断:通过静态类型推断实现完全类型安全,验证从字符串到复杂对象的所有内容。 性能:采用模块化设计,可以更好的利用 tree-shaking,压缩后体积只有 300b,相比 Zod,体积更小。
Zod 代码示例:
import { string } from 'zod';
const emailValidator = string().email();
Zod 的这种 API 设计的一个问题是,在导入 string()
函数时,所有与字符串相关的验证器(IP 地址、UUID 等)都将包含在您的包中,无论我们是否使用这些功能。这会造成不必要的膨胀,并可能影响应用程序的性能,特别是当它在客户端使用时,客户端对包大小更加敏感。
Valibot 代码示例:
import { email, string } from 'valibot';
const emailValidator = string([email()]);
Valibot 的 string()
这里的函数仅包含检查输入数据是否为字符串的最低限度,而没有其他内容。附加功能是通过导入其他验证器来启用的,Valibot 将其称为“管道”。这确保了我们的捆绑包中只包含必要的逻辑,这也是 bundle 包体积更小的主要原因之一。
Zod 迁移 Valibot 快速指南
下面以一个同时用于客户端和服务器端的例子验证为例说明。
Zod 代码:
import { z } from 'zod';
const baseContactFormSchema = z.object({
name: z.string().min(1, 'Name is required'),
message: z.string().min(1, 'Message is required'),
});
export const contactFormSchema = z.discriminatedUnion('showInGuestbook', [
baseContactFormSchema.extend({
email: z
.string()
.min(1, 'Email is required')
.email('Not a valid email'),
subject: z.string().min(1, 'Subject is required'),
showInGuestbook: z.literal(false),
}),
baseContactFormSchema.extend({
email: z
.string()
.email('Not a valid email')
.optional()
.or(z.literal('')),
subject: z.string().optional(),
showInGuestbook: z.literal(true),
}),
]);
export type ContactFormData = z.infer<typeof contactFormSchema>;
迁移后的 valibot 代码:
import {
Output,
email,
literal,
merge,
minLength,
object,
optional,
string,
union,
variant,
} from 'valibot';
const baseContactFormSchema = object({
name: string([minLength(1, 'Name is required')]),
message: string([minLength(1, 'Message is required')]),
});
export const contactFormSchema = variant('showInGuestbook', [
merge([
baseContactFormSchema,
object({
email: string([
minLength(1, 'Email is required'),
email('Not a valid email'),
]),
subject: string([minLength(1, 'Subject is required')]),
showInGuestbook: literal(false),
}),
]),
merge([
baseContactFormSchema,
object({
email: optional(
union([string([email()]), literal('')], 'Not a valid email'),
),
subject: optional(string()),
showInGuestbook: literal(true),
}),
]),
]);
export type ContactFormData = Output<typeof contactFormSchema>;
从上面迁移前后的代码看,最主要的区别就是 valibot 的模块化设计,每个模块都有单一职责。迁移前后的的 bundle size 对比:
- Zod: 12.37 KB Gzipped
+ Valibot: 1.72 KB Gzipped
体积缩小了近 90%!
显然,上面的用例代码并不是最复杂的,而且随着使用的验证器越来越多,差异的会逐渐放大。
结论
过去的6个月,Valibot 增长很快。Valibot 很有潜力成为 Zod/Yup 的替代品,但文档质量是其最大的障碍。很多开发者开始尝试使用 Valibot,同时享受较小的捆绑包。如果你有新的内部项目,建议你试试看。但对于更大、更严肃的项目来说,采用 Valibot 可能还为时过早,因为文档的缺乏是一个不容忽视的风险。
展望未来,个人建议作者和贡献者能够花一些精力在文档建设上。现在的功能和 API 已经支持的不错了,足以和现有的产品竞争。文档完善之后,可以更好地让开发者懂得如何使用它们。
大家都在看