ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

TanStack Router:retainSearchParams 搜索中间件——让 Search Params 在导航间自动保留

TanStack Router:retainSearchParams 搜索中间件——让 Search Params 在导航间自动保留 TanStack RouterretainSearchParams 搜索中间件——让 Search Params 在导航间自动保留【免费下载链接】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在 TanStack Router 中每次客户端导航link/navigate都会按目标路由重新解析 search params当前 URL 中的查询参数如分页页码、筛选条件、会话标识很容易被丢掉。retainSearchParams是一个开箱即用的search middleware把它挂到路由的search.middlewares上后指定的搜索参数或全部参数会在后续导航中自动保留无需在每处to/search里手动透传。本文基于仓库中 retainSearchParamsFunction.md 的官方文档结合 router-core 源码 与测试用例完整讲解它的用法、参数语义和底层工作原理。读完本文你将能够在 code-basedcreateRootRoute/createRoute与 file-basedcreateFileRoute路由上正确配置retainSearchParams理解它true与 key 数组两种入参的行为差异以及显式导航值优先的保留规则看懂SearchMiddleware类型、中间件执行管线与meta元数据在参数保留/剔除中的作用。retainSearchParams 的入参true 或 key 列表retainSearchParams接受两种形式的keystrue保留当前 search 中的所有参数Arraykeyof TSearchSchema只保留列表里列出的 key类型上被约束为当前路由 search schema 的 key。示例一保留指定 key根路由code-based官方文档给出的第一个示例是在根路由上保留rootValueimport { z } from zod import { createRootRoute, retainSearchParams } from tanstack/react-router const searchSchema z.object({ rootValue: z.string().optional(), }) export const Route createRootRoute({ // Zod v4 可以直接传 schema validateSearch: searchSchema, search: { middlewares: [retainSearchParams([rootValue])], }, })这里middlewares是search配置项下的数组retainSearchParams([rootValue])表示后续任何导航中只要目标 search 没有显式指定rootValue就沿用当前 URL 里的rootValue。示例二保留全部参数file-based 路由第二个示例演示了true入参以及 file-based 路由写法import { z } from zod import { createFileRoute, retainSearchParams } from tanstack/react-router const searchSchema z.object({ one: z.string().optional(), two: z.string().optional(), }) export const Route createFileRoute(/)({ // Zod v4 可以直接传 schema validateSearch: searchSchema, search: { middlewares: [retainSearchParams(true)], }, })传true时one、two等全部参数在导航后都会保留显式覆盖的除外。注意两点适用前提validateSearch使用 Zod 时文档注释明确提示Zod v4 可以直接使用 schema旧版 Zod 通常需要包一层zodValidator(searchSchema)仓库文档中以 Zod v4 为准retainSearchParams挂在哪个路由上就在该路由参与的目标路由链路中生效。中间件会按目标路由链从外到内收集见下文applySearchMiddleware源码分析因此把全局性的保留逻辑放在根路由上是最常见的做法。源码解析retainSearchParams 的实现retainSearchParams的核心实现在 searchMiddleware.tsrouter-core包React / Solid / Vue 三端均从router-core再导出例如 react-router/src/index.tsxexport function retainSearchParamsTSearchSchema extends object( keys: Arraykeyof TSearchSchema | true, ): SearchMiddlewareTSearchSchema { return ({ search, next }) { const { search: resultSearch, meta } ( next as unknown as SearchMiddlewareNextWithMetaTSearchSchema )(search, true) if (keys true) { const copy { ...search, ...resultSearch } // 1) 处理被默认值填充/被移除的 key显式导航值或值未变化时删除 // 让保留逻辑尊重导航方显式给出的参数 // 2) meta.removedAny 中的 key 无条件删除 // 3) 被 validateSearch 用默认值填充的 key如果当前 search 有真实值 // 则用当前值覆盖默认值 return copy } // keys 数组分支以导航结果 resultSearch 为底 // 把列表中的 key 从当前 search 补回去同样尊重显式值与默认值 const copy { ...resultSearch } // ... 逐 key 补回逻辑 return copy } }关键设计点不可变不 mutate两个分支都先展开成新对象{ ...search, ...resultSearch }或{ ...resultSearch }从不修改传入的search。router-core 单元测试专门验证了这一点retainSearchParams([id, filter])执行后原 search 对象保持不变返回{ id: 1, filter: active, page: new }。显式导航值优先源码中通过meta.explicit本次导航显式声明的 search与deepEqual比对判断——如果导航显式设置了某个参数哪怕与当前值相同就不会被保留覆盖这保证了业务代码永远可以用显式传参打断保留行为。与 schema 默认值协同meta.defaulted记录哪些 key 是被validateSearch用默认值如 Zod 的.default()填充的retain 会用当前 URL 中的真实值替换这些默认值避免默认值把保留的参数顶掉。中间件管线SearchMiddleware 类型与执行顺序类型定义中间件相关类型定义在 route.tsexport type SearchMiddlewareMeta { removed?: Mapstring, unknown // 被按值移除的 key 及其默认值 removedAny?: Setstring // 被无条件移除的 key defaulted?: Mapstring, unknown // 被 schema 默认值填充的 key explicit?: unknown // 本次导航显式声明的 search 结果 } export type SearchMiddlewareContextTSearchSchema { search: TSearchSchema // 当前 URL 的 search next: (newSearch: TSearchSchema) TSearchSchema // 调用后续中间件 meta?: SearchMiddlewareMeta } export type SearchMiddlewareTSearchSchema ( ctx: SearchMiddlewareContextTSearchSchema, ) TSearchSchema这是一个经典的洋葱模型search是当前 URL 的参数next把可能被修改过的search 传给后续中间件最终由路由配置的search函数式更新dest.search产生导航声明的目标参数。retainSearchParams内部调用next(search, true)第二个参数collectMeta: true拿到后续管线算出的目标 search 元数据再在此基础上做保留——即它站在导航结果之前做后处理只补回它负责的 key。执行管线 applySearchMiddleware管线装配与执行逻辑在 router.ts 的applySearchMiddleware中导航构建目标 location 时调用调用点见 router.ts#L2063收集中间件遍历目标路由链destRoutes依次push每个路由routeOptions.search.middlewares旧的preSearchFilters/postSearchFilters会被包装成等价中间件兼容源码注释标明将在 v2 移除追加 validate 中间件当路由配置了validateSearch时动态追加一个 validate 中间件——它先next(search)拿到导航结果再执行 schema 校验/解析并把结果中出现但输入中没有的 key记录到meta.defaulted即被默认值填充的参数最后返回{ ...result, ...validated }。这解释了上文与 schema 默认值协同的行为来源末端落地中间件链耗尽后index middlewares.length若dest.search为函数/对象则执行functionalUpdate(dest.search, currentSearch)得到最终声明值并写入meta.explicit。从源码结构看这意味着retainSearchParams拿到的resultSearch是本次导航显式声明 schema 校验后的结果而meta.explicit/meta.defaulted让它能区分导航显式给的值和schema 默认值从而实现精确的保留语义。行为验证官方测试用例router-core 单元测试searchMiddleware.test.ts 覆盖了三种关键行为数组 key next 不返回这些 keyretainSearchParams([id, filter])下当前 search 为{ id: 1, filter: active, page: 2 }、导航结果为{ page: new }时最终得到{ id: 1, filter: active, page: new }——保留项被补回导航项正常更新true全量保留当前 search 为{ id: 1, filter: active }、导航结果为{ id: 2 }时retainSearchParams(true)输出{ id: 2, filter: active }——显式更新的id生效未被提及的filter被保留同引用复用安全同一 search 对象多次调用中间件结果一致且不产生变异。三端集成测试真实导航场景react-router 的集成测试 用真实路由根路由//posts验证初始 search 为{ value: abc }点击 link 导航到/posts后router.state.location.search与 URL 中valueabc仍然存在should retain value search param初始 search 为空时retainSearchParams([value])什么都不做导航后 search 为空should do nothing if value search param is not set。Solid 与 Vue 端拥有同构的集成测试solid-router/tests/searchMiddleware.test.tsx 与 vue-router/tests/searchMiddleware.test.tsx说明该中间件行为在三个框架绑定层保持一致。与 stripSearchParams 的对照与retainSearchParams同文件实现的 stripSearchParams 方向相反它在导航时剔除参数支持true全量剔除、key 数组、以及值等于默认值才剔除的对象形式并通过meta.removed/meta.removedAny把剔除信息暴露给后续中间件——retainSearchParams正是读取这两个字段来决定保留的 key 是否已被上游明确移除。两者组合使用可以精细控制 URL 的带什么、丢什么其用法详见 stripSearchParamsFunction.md。小结与适用要点要点说明挂载位置路由选项search.middlewares数组根路由挂全局保留子路由挂局部保留入参true保留全部或keyof TSearchSchema数组保留指定 keykey 受 search schema 类型约束显式优先导航中显式声明的参数会覆盖保留逻辑由meta.explicit判定默认值协同被 schema 默认值填充的 key 会用当前 URL 真实值还原由meta.defaulted判定不可变中间件不修改原 search 对象测试中有专门断言框架一致性实现在router-coreReact / Solid / Vue 三端同构导出、各有集成测试如果你希望在跨页导航中保持分页、筛选、来源追踪等 query 参数不丢失retainSearchParams就是 TanStack Router 提供的官方手段一行middlewares: [retainSearchParams([...])]即可声明底层由 searchMiddleware.ts 与 router.ts 中的中间件管线 共同保证正确性。【免费下载链接】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),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进