
TanStack Router 搜索参数校验适配器实战指南Zod / Valibot / ArkType 深度解析【免费下载链接】router A client-first, server-capable, fully type-safe router and full-stack framework for the web (React and more).项目地址: https://gitcode.com/GitHub_Trending/ro/router本指南以 search-validator-adapters 示例 为核心系统讲解 TanStack Router 中validateSearch的搜索参数校验机制以及如何通过官方适配器包将 Zod、Valibot、ArkType 三种主流 schema 校验库无缝接入路由获得端到端的类型安全 URL 状态管理。读完本文你将掌握三类适配器的接入姿势、fallback兜底模式的底层实现原理以及校验与 React Query 数据预取的组合用法可直接迁移到自己的项目中。示例概览一个页面、三套校验方案examples/react/search-validator-adapters是一个基于 Vite React TypeScript 的最小演示应用页面提供一个搜索框用户输入关键字后URL 中的?searchxxx会被实时同步并驱动一个基于 React Query 的用户列表查询。同一套交互分别用 Zod、Valibot、ArkType 三套 schema 校验方案实现了三条等价路由路由校验库适配器包核心文件/users/zod/Zodtanstack/zod-adapterzod.index.tsx/users/valibot/Valibottanstack/valibot-adaptervalibot.index.tsx/users/arktype/ArkTypetanstack/arktype-adapterarktype.index.tsx三个页面的导航入口统一由 Header.tsx 中的三个Link提供方便直接在浏览器中对比三种校验库的写法差异。快速启动安装、开发与构建该示例与仓库内其他示例一样使用 pnpm 管理依赖脚本定义在 package.json# 安装依赖 pnpm install # 启动开发服务器vite --port 3000 pnpm dev # 生产构建 全量类型检查NODE_OPTIONS--max-old-space-size4096 tsc --noEmit pnpm build # 运行单元测试与类型测试vitesttypecheck 已启用 pnpm test:unit其中build脚本在vite build之后还会以tsc --noEmit做一次全量类型检查这正是该示例强调类型安全的体现——如果搜索参数的推断类型有误构建阶段就会直接报错。测试运行器由 vite.config.ts 配置测试目录为./tests环境为jsdom并开启了typecheck以同时执行.test-d.tsx类型测试。核心机制validateSearch与 URL 状态管理在 TanStack Router 中URL 的 query string 并不是字符串而是被建模为强类型的路由状态。每条路由都可以通过validateSearch选项声明一个校验器路由器会在每次导航、前进后退时对?search...进行解析和校验把字符串化的 URL 参数还原为类型安全的对象这就是示例 README 中提到的 URL state management。validateSearch的完整定义记录在 RouteOptionsType.md 中其底层契约是一个ValidatorAdapter接口types声明校验器的输入类型URL 中解析出的原始形态与输出类型校验后组件的使用形态parse接收 URL 解析出的原始值执行 schema 解析并返回结果。tanstack/zod-adapter、tanstack/valibot-adapter、tanstack/arktype-adapter三个包分别把各自生态的 schema 包装成这个接口因此无论底层用哪个校验库路由层代码都能以统一方式工作。示例的应用入口 main.tsx 通过createRouter装配路由树并开启scrollRestoration: true让搜索参数变化时页面滚动位置也能正确恢复。从底层实现看校验发生在类型层面与运行时层面两个维度类型层面路由器基于ValidatorAdapter的types推断出Route.useSearch()、navigate({ search })、Link的searchprop 等全部 API 的入参/出参类型运行时层面parse负责真正把?search...字符串解析并校验成对象。二者缺一不可——这也就是示例中同名路由要分别放在zod、valibot、arktype三个目录下的原因每条路由的搜索参数类型彼此独立、互不干扰。Zod 适配器zodValidator与fallback兜底Zod 路由的完整实现见 zod.index.tsxconst fallbackString fallback as unknown as ( schema: z.ZodString, fallback: string, ) z.ZodTypestring, z.ZodTypeDef, string export const Route createFileRoute(/users/zod/)({ validateSearch: zodValidator( z.object({ search: fallbackString(z.string(), ).default(), }), ), ... })关键点是zodValidator与fallback的组合。查看 zod-adapter 源码zodValidator接受一个 Zod schema或{ schema, input, output }选项对象并完成两件事从schema._input/schema._output提取类型按input/output选项组装成ValidatorAdapter的types。默认input: input、output: output即 URL 侧接收 Zod 的输入类型、组件侧使用输出类型若设置{ input: output }则可让 URL 侧直接使用输出类型。parse直接调用schema.parse(input)把 Zod 的解析能力桥接到路由层。fallback是官方提供的一个宽松输入 严格兜底工具其实现zod-adapter/src/index.ts是一个管道类型return z.customTSchema[_input]().pipe(schema.catch(fallback))含义是先用z.custom接受任意输入保证 URL 中参数缺失、类型错误都不会在解析阶段抛错再通过schema.catch(fallback)在解析失败时回落到兜底值。配合.default()即使 URL 里完全没有search参数最终得到的也是空字符串而不会出现undefined导致的渲染崩溃。由于该管道返回的是ZodPipeline类型示例中通过fallbackString这个类型断言将其收窄为普通的ZodTypestring让z.object({ search: ... })的类型推断保持整洁——这是实践中值得借鉴的细节。Valibot 适配器极简的 schema 直传Valibot 路由见 valibot.index.tsx写法比 Zod 更简洁——无需任何包装函数schema 对象直接作为validateSearchexport const Route createFileRoute(/users/valibot/)({ validateSearch: v.object({ search: v.fallback(v.optional(v.string(), ), ), }), ... })这里v.fallback(v.optional(v.string(), ), )与 Zod 版的fallback语义完全对应v.optional(v.string(), )让参数缺省时取空串外层v.fallback再兜底非法输入。Valibot 的 schema 之所以能直接使用是因为tanstack/valibot-adapter提供了valibotValidator包装器valibot-adapter/src/index.tsexport const valibotValidator TOptions extends GenericSchema( options: TOptions, ): ValibotValidatorAdapterTOptions { return { types: { input: null, output: null }, parse: (input) parse(options, input), } }其parse内部调用 Valibot 的parse(options, input)类型层面则借助InferInput/InferOutput从GenericSchema提取输入与输出类型。值得注意的是 Valibot 是模块化设计的库——示例中只引入了object、fallback、optional、string这几个用到的校验器打包体积天然可控这也正是 README 强调多种校验器适配器的实用价值之一。ArkType 适配器类型优先的原生 schemaArkType 路由见 arktype.index.tsx它是三种方案中类型表达最直接的一个const search type({ search: string , }) export const Route createFileRoute(/users/arktype/)({ validateSearch: search, ... })ArkType 用字符串 DSL 描述 schema——string 一行就同时声明了字段类型为 string与默认值为空串schema 对象直接作为validateSearch传入。这是因为 ArkType 的type对象在结构上恰好满足路由器的适配器契约inferIn/infer对应输入/输出类型assert承担解析校验。官方同时也提供了显式的arkTypeValidator包装arktype-adapter/src/index.ts它把inferIn/infer提取为types、以assert作为parse两种方式效果等价显式包装在需要强约束类型边界时更推荐。类型安全如何落地从Link到useSearch搜索参数的类型安全不仅作用于路由内部还会反向传播到所有导航入口。示例在 tests/arktype.test-d.tsx 中用类型级测试锁死了这一行为expectTypeOf(Linktypeof router, string, /users/arktype) .parameter(0) .toHaveProperty(search) .excludeboolean | ((...args: ReadonlyArrayany) any)() .toEqualTypeOf{ search?: string } | undefined() expectTypeOf(ArkTypeRoute.useSearch()).toEqualTypeOf{ search: string }()测试断言了三个关键事实指向/users/arktype/的Link组件其searchprop 的类型被精确推断为{ search?: string }路由的useSearch()返回{ search: string }——由于 schema 声明了默认值输出类型中search不再是可选由于tanstack/react-router的类型注册见 main.tsx 中的declare module这些类型推断对整个应用全局生效。这意味着如果某个组件误传了{ search: number }或者把search写成serachTypeScript 会在编辑器和pnpm build的类型检查阶段直接报错——URL 参数的拼写错误从运行时错误提前到了编译期。tests目录下还配套了 arktype.test.tsx、valibot.test-d.tsx、valibot.test.tsx 等运行时与类型双重测试vite.config.ts中的typecheck: { enabled: true }确保两类测试在同一命令下执行。与 React Query 集成search 参数驱动的数据预取示例真正的实战价值在于把校验后的搜索参数与数据层串联起来。三条路由的loader部分结构一致以 Zod 版为例loaderDeps: (opt) ({ search: opt.search }), loader: (opt) { opt.context.queryClient.ensureQueryData( usersQueryOptions(opt.deps.search.search ?? ), ) },配合 Users.tsx 中定义的 query optionsexport const usersQueryOptions (search: string) queryOptions({ queryKey: [users, search], queryFn: async () searchUsers(search), })这条链路完整展示了 TanStack Router 的搜索参数即状态哲学loaderDeps声明 loader 依赖的搜索参数子集。?search变化时loader 会带着新的opt.deps.search重新执行loaderensureQueryData在路由渲染前就把用户列表查询结果预取进 QueryClient 缓存组件侧再用useSuspenseQuery同步读取配合React.Suspense实现零闪烁的加载体验queryKey: [users, search]搜索关键字成为查询键的一部分不同关键字天然对应不同缓存条目输入防抖、切换回退都不必额外处理缓存失效路由上下文queryClient通过 __root.tsx 的createRootRouteWithContextContext()注入并在 main.tsx 创建时挂载到createRouter的context中loader 中经opt.context.queryClient访问。交互侧Search.tsx 的onChange调用navigate({ search: { search }, replace: true })——replace: true避免每次输入都产生新的历史记录保证浏览器前进/后退键的体验符合直觉。三条路由在 Header.tsx 中切换时各自的搜索参数状态独立保存你可以直接在浏览器中验证切走再切回搜索词仍然保留这一 URL 状态管理的核心体验。小结通过这一个示例你可以一次性对照掌握三种主流 schema 校验库在 TanStack Router 中的接入方式ZodzodValidator(z.object({...}))显式包装fallback管道实现任意输入 失败兜底适配器包还支持input/output类型方向的精细控制源码见 zod-adapter/src/index.tsValibotschema 即插即用模块化引入按需打包valibotValidator内部以parse(options, input)桥接源码见 valibot-adapter/src/index.tsArkType字符串 DSL 一行声明类型与默认值schema 对象天然满足适配器契约也提供显式arkTypeValidator包装源码见 arktype-adapter/src/index.ts。三者共享同一套路由器契约ValidatorAdaptertypesparse因此接入成本低、可替换性强而搜索参数的类型会从validateSearch一路传播到Link、useSearch、navigate和 loader 依赖最终由 React Query 驱动数据预取构成一个完整的强类型 URL 状态 服务端数据闭环。若想深入学习validateSearch的完整选项语义可继续阅读 RouteOptionsType.md参考其他文件路由示例如 basic-file-based还能看到该校验机制在真实路由树中的更多组合方式。【免费下载链接】router A client-first, server-capable, fully type-safe router and full-stack framework for the web (React and more).项目地址: https://gitcode.com/GitHub_Trending/ro/router创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考