首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >TanStack Router:搜索参数即状态!

TanStack Router:搜索参数即状态!

作者头像
萌萌哒草头将军
发布2025-06-07 13:42:17
发布2025-06-07 13:42:17
10700
代码可运行
举报
文章被收录于专栏:前端框架前端框架
运行总次数:0
代码可运行

前言

之前介绍过 TanStack,最近发现 TanStack Router 更新了一篇博客,介绍了安全且高效解析搜索参数的新理念!今天分享给大家~,下面是翻译之后的原文!

正文

搜索参数即状态

搜索参数历来被视为二等状态。它们是全局的、可序列化的、可共享的——但在大多数应用程序中,它们仍然通过字符串解析、松散的约定和脆弱的实用工具拼凑而成。

即使是像验证 sort 参数这样简单的事情,也很快变得冗长:

代码语言:javascript
代码运行次数:0
运行
复制
const schema = z.object({
  sort: z.enum(['asc', 'desc']),
})

const raw = Object.fromEntries(new URLSearchParams(location.href))
const result = schema.safeParse(raw)

if (!result.success) {
  // 回退、重定向或显示错误
}

这种方法有效,但它是手动的且重复的。没有类型推断、与路由本身没有关联,而且一旦你想添加更多类型、默认值、转换或结构,它就会崩溃。

更糟糕的是,URLSearchParams 只支持字符串。它不支持嵌套 JSON、数组(除了简单的逗号分割外)或类型强制转换。因此,除非你的状态是扁平且简单的,否则你很快就会遇到瓶颈。

这就是为什么我们开始看到工具和提案的兴起——比如 Nuqs、Next.js RFCs 和用户自定义模式——旨在使搜索参数更具类型安全性和人体工程学。这些大多专注于改进从 URL 的 _读取_。

但几乎没有哪一个解决了更深层次、更困难的问题:写入 搜索参数,以安全和原子化的方式,充分了解路由上下文。

写入搜索参数是问题所在

从 URL 读取是一回事。从代码中构建一个有效且有意的 URL 是另一回事。

当你尝试这样做时:

代码语言:javascript
代码运行次数:0
运行
复制
<Link to="/dashboards/overview" search={{ sort: 'asc' }} />

你会意识到你根本不知道这个路由支持哪些搜索参数,或者你是否正确地格式化了它们。即使有助手来将它们字符串化,也没有任何机制来强制调用者和路由之间的契约。没有类型推断、没有验证、没有护栏。

这就是 约束成为特性 的地方。

如果不在路由本身中明确声明搜索参数模式,你就只能猜测。你可能在一个地方进行了验证,但没有什么能阻止另一个组件使用无效、部分或冲突的状态进行导航。

约束是协调成为可能的关键。它使 非本地调用者 能够安全参与。

本地抽象有所帮助 —— 但它们无法协调

Nuqs 这样的工具是本地抽象如何改善搜索参数处理 人体工程学 的绝佳例子。你可以获得基于 Zod 的解析、类型推断,甚至是可写的 API——所有这些都限定在特定组件或钩子中。

它们使在 隔离 中读写搜索参数变得更容易——这很有价值。

但它们无法解决更广泛的 协调 问题。你仍然会遇到重复的模式、分散的期望,以及无法在路由或组件之间强制一致性的问题。默认值可能冲突。类型可能漂移。当路由演变时,没有什么能保证所有调用者都会随之更新。

这才是真正的碎片化问题——解决它需要将搜索参数模式引入路由层本身。

TanStack Router 如何解决这个问题

TanStack Router 提供了整体解决方案。

你无需在应用程序中分散模式逻辑,而是 在路由本身中定义它

代码语言:javascript
代码运行次数:0
运行
复制
export const Route = createFileRoute('/dashboards/overview')({
  validateSearch: z.object({
    sort: z.enum(['asc', 'desc']),
    filter: z.string().optional(),
  }),
})

这个模式成为唯一的真相来源。你在任何地方都能获得完整的推断、验证和自动补全:

代码语言:javascript
代码运行次数:0
运行
复制
<Link
  to="/dashboards/overview"
  search={{ sort: 'asc' }} // 完全类型化,完全验证
/>

想只更新部分搜索状态?没问题:

代码语言:javascript
代码运行次数:0
运行
复制
navigate({
  search: (prev) => ({ ...prev, page: prev.page + 1 }),
})

它是 reducer 风格的、事务性的,并直接与路由器的响应性模型集成。组件仅在它们使用的特定搜索参数发生变化时才会重新渲染——而不是每次 URL 发生变化时。

TanStack Router 如何防止模式碎片化

当你的搜索参数逻辑存在于用户空间——分散在钩子、实用工具和助手函数中——不可避免地会出现 冲突的模式

也许一个组件期望 sort: 'asc' | 'desc'。另一个添加了 filter。第三个假定 sort: 'desc' 为默认值。它们之间没有共享的真相来源。

这会导致:

  • 不一致的默认值
  • 冲突的格式
  • 设置了其他组件无法解析的值的导航
  • 损坏的深度链接和无法追踪的错误

TanStack Router 通过将模式直接绑定到路由定义——以 层级方式 防止这种情况。

父路由可以定义共享的搜索参数验证。子路由继承该上下文,以类型安全的方式添加或扩展它。这使得在应用程序的不同部分意外创建重叠、不兼容的模式变得 _不可能_。

示例:安全的分层搜索参数验证

以下是实际操作方式:

代码语言:javascript
代码运行次数:0
运行
复制
// routes/dashboard.tsx
export const Route = createFileRoute('/dashboard')({
  validateSearch: z.object({
    sort: z.enum(['asc', 'desc']).default('asc'),
  }),
})

然后,子路由可以安全地扩展该模式:

代码语言:javascript
代码运行次数:0
运行
复制
// routes/dashboard/$dashboardId.tsx
export const Route = createFileRoute('/dashboard/$dashboardId')({
  validateSearch: z.object({
    filter: z.string().optional(),
    // ✅ `sort` 从父路由自动继承
  }),
})

当你匹配 /dashboard/123?sort=desc&filter=active 时,父路由验证 sort,子路由验证 filter,一切无缝协作。

尝试在子路由中将所需的父参数重新定义为完全不同的内容?会触发类型错误。

代码语言:javascript
代码运行次数:0
运行
复制
validateSearch: z.object({
  // ❌ 类型错误:布尔值无法扩展父路由的 'asc' | 'desc'
  sort: z.boolean(),
  filter: z.string().optional(),
})

这种强制执行使嵌套路由既可组合又安全——这是一种罕见的组合。

内置纪律

这里的魔法在于,你无需教导团队遵循约定。路由 拥有 模式。每个人只需使用它。没有重复。没有漂移。没有无声的错误。没有猜测。

当你将验证、类型化和所有权引入路由器本身时,你就不再将 URL 当作字符串,而是开始将其视为真正的状态——因为它们就是状态。

搜索参数即状态

大多数路由系统将搜索参数视为事后补充。你 可以 读取它们,也许可以解析,也许可以字符串化,但很少有你真正可以 信任 的东西。

TanStack Router 颠倒了这一观念。它使搜索参数成为路由契约的核心部分——经过验证、可推断、可写且具有响应性。

因为如果你不将搜索参数视为状态,你就会不断泄露它、破坏它并绕过它。

最好从一开始就正确对待它。

如果你对将搜索参数作为一等状态的可能性感到好奇,我们邀请你尝试 TanStack Router。体验在路由逻辑中验证、可推断和响应性搜索参数的力量。

最后

搜索参数很可能导致类型安全和协调等问题。TanStack Router 提供了一种解决方案,将搜索参数模式整合到路由定义中,实现验证、推断和响应性,从而防止模式碎片化并确保安全的分层验证!

原文链接:https://tanstack.com/blog/search-params-are-state

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-06-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 萌萌哒草头将军 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 正文
    • 搜索参数即状态
    • 写入搜索参数是问题所在
    • 本地抽象有所帮助 —— 但它们无法协调
    • TanStack Router 如何解决这个问题
    • TanStack Router 如何防止模式碎片化
    • 示例:安全的分层搜索参数验证
    • 内置纪律
    • 搜索参数即状态
  • 最后
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档