资讯详情

TanStack Router 导航(Navigation)完全指南:类型安全的 Link、预加载、导航拦截与滚动恢复

📅 2026/9/15 17:15:23 | 华诺云谱 👁 阅读
TanStack Router 导航(Navigation)完全指南:类型安全的 Link、预加载、导航拦截与滚动恢复
TanStack Router 导航Navigation完全指南类型安全的 Link、预加载、导航拦截与滚动恢复【免费下载链接】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 的 router-core 导航子技能文档packages/router-core/skills/router-core/navigation/SKILL.md系统讲解客户端路由框架中导航体系的核心 API 与最佳实践类型安全的Link/useNavigate/Navigate/router.navigate、基于from的相对导航、activeOptions激活态控制、三种预加载策略、useBlocker导航拦截、linkOptions/createLink扩展机制、滚动恢复以及MatchRoute待定 UI。读完本文你将掌握在 TanStack RouterReact/Solid/Vue 实现中构建可靠、可访问、性能友好导航层的完整实战方案并理解其类型系统与底层源码的运作原理。概览导航体系的技术组成导航Navigation是路由器的核心交互面。从源码结构看TanStack Router 的导航能力横跨多个模块类型层集中在 packages/router-core/src/link.tsLinkOptions、ActiveOptions、preload等类型定义运行时层由 packages/router-core/src/router.ts 的router.navigate、router.preloadRoute与历史栈拦截逻辑支撑React 实现则在 packages/react-router/src/link.tsxLink、createLink、linkOptions。围绕导航本文按以下脉络展开基础导航组件Link、useNavigate、Navigate、router.navigate相对导航与from解析规则激活态控制activeOptions/activeProps预加载preloading三种策略与全局配置导航拦截useBlocker/Block选项复用与自定义组件linkOptions/createLink滚动恢复scroll restoration待定 UIMatchRoute与常见错误清单基础导航四种导航入口类型安全的 Link 组件导航的最基本形式是Link组件。它渲染真实的a标签并生成可访问的href同时提供全类型安全的to与paramsimport { Link } from tanstack/react-router function PostLink({ postId }: { postId: string }) { return ( Link to/posts/$postId params{{ postId }} View Post /Link ) }这里to被约束为路由树中真实存在的路径由ToPathOption/ConstrainLiteral类型推导见 link.tsparams则必须包含路径中$postId声明的动态段。路径参数用$前缀声明必须通过params选项传入这是本技能文档标注为 CRITICAL 级别的铁律详见下文常见错误。useNavigate面向副作用驱动的编程式导航useNavigate返回一个navigate函数用于在副作用中触发导航如表单提交、异步操作完成后跳转。对于用户可点击的元素应优先使用Link保留href、支持 cmd/ctrl点击、预加载与无障碍语义import { useNavigate } from tanstack/react-router function CreatePostForm() { const navigate useNavigate({ from: /posts }) const handleSubmit async (e: React.FormEvent) { e.preventDefault() const response await fetch(/api/posts, { method: POST, body: ... }) const { id: postId } await response.json() if (response.ok) { navigate({ to: /posts/$postId, params: { postId } }) } } return form onSubmit{handleSubmit}{/* ... */}/form }useNavigate({ from: /posts })将导航的解析基准固定在/posts路由从而支持相对to。从源码看useNavigate与Link共享同一套ToPathOption/PathParamOptions/SearchParamOptions类型推导link.ts因此params是否必填由目标路由自动推断。Navigate 组件与 router.navigateNavigate组件在挂载时立即执行一次客户端导航适用于遗留 URL 重定向等声明式场景import { Navigate } from tanstack/react-router function LegacyRedirect() { return Navigate to/posts/$postId params{{ postId: my-first-post }} / }而router.navigate是路由器实例上的方法只要持有 router 实例即可调用包括 React 组件树之外如事件总线、工具函数、服务端代码。这四者共享同一套导航选项类型to/params/search/from的规则完全一致。相对导航与 from 解析规则不提供from时导航从根路径/解析——这意味着to..会被当作相对根路径的上级而不是当前路由的上级。要使用../.这类相对路径必须通过from指明基准路由import { createFileRoute, Link } from tanstack/react-router export const Route createFileRoute(/posts/$postId)({ component: PostComponent, }) function PostComponent() { return ( div {/* Relative to current route */} Link from{Route.fullPath} to.. Back to Posts /Link {/* . reloads the current route */} Link from{Route.fullPath} to. Reload /Link /div ) }Route.fullPath是由createFileRoute生成的路由对象的完整路径常量直接作为from的取值即可。从类型系统看from被约束为路由树中的真实路径FromPathOption见 link.ts并且from的提供会收窄to的自动补全范围——这正是技能文档Cross-References中提到的与 type-safety 技能的关联点。关键事实没有from时只有绝对路径才能获得类型自动补全与校验相对路径如..会从根解析导致跳转行为与直觉不符。激活态控制activeProps / inactiveProps / activeOptions导航栏高亮当前路由是常见需求。Link提供activeProps与inactiveProps在激活/非激活状态下以 props 形式叠加到a元素上import { Link } from tanstack/react-router function NavLink() { return ( Link to/posts activeProps{{ className: font-bold }} inactiveProps{{ className: text-gray-500 }} activeOptions{{ exact: true }} Posts /Link ) }此外激活的链接会自动带上data-statusactive和aria-currentpage两个静态属性见 packages/react-router/src/link.tsx 的STATIC_ACTIVE_PROPS因此你也可以完全用 CSS 属性选择器如a[data-statusactive]来定义高亮样式无需写任何 JS 逻辑。activeOptions控制激活的匹配规则其类型定义在 link.ts选项默认值说明exactfalse为true时仅当当前路由与to精确匹配才激活不含子路由includeHashfalse是否将 URL hash 纳入激活匹配includeSearchtrue是否将 search 参数纳入激活匹配当前 URL search 需包含式匹配searchpropexplicitUndefinedfalse修改includeSearch行为为true时search中显式为undefined的属性必须不出现在当前 URL 中链接才算激活Link的子节点也支持函数形式直接接收isActive布尔值便于在激活时渲染不同的内容或样式Link to/posts {({ isActive }) span className{isActive ? font-bold : }Posts/span} /Link该能力对应 link.tsx 中 children 的类型定义((state: { isActive: boolean }) React.ReactNode) | React.ReactNode。预加载Preloading三种策略与全局配置预加载让路由的 loader 数据/代码在真正导航前就被拉取显著降低跳转后的等待时间。技能文档定义了三种策略其类型定义见 link.tsintent—— 用户聚焦focus、悬停hover或触摸touch链接时预加载触摸意图立即触发focus/hover 受preloadDelay延迟控制若在延迟结束前移开则取消viewport—— 链接进入视口时IntersectionObserver预加载render—— 链接一渲染就预加载false—— 关闭预加载。全局默认配置在创建 router 时统一设置默认策略与延迟import { createRouter } from tanstack/react-router const router createRouter({ routeTree, defaultPreload: intent, defaultPreloadDelay: 50, // ms默认就是 50 })这些选项对应 router.ts 中的defaultPreload、defaultPreloadDelay源码默认值 50ms见defaultPreloadDelay: 50的初始化、defaultPreloadIntentProximity、defaultPreloadStaleTime与defaultPreloadGcTime。其中defaultPreloadIntentProximity控制intent策略下光标距离链接多近才触发。按链接覆盖每个Link可独立覆盖策略与延迟Link to/posts/$postId params{{ postId }} preloadintent preloadDelay{100} View Post /Link数据新鲜度窗口预加载的数据默认在 30 秒内保持新鲜defaultPreloadStaleTime: 30_000窗口期内不会重新拉取。如果项目使用 TanStack Query 等外部缓存管理数据新鲜度应将defaultPreloadStaleTime: 0设为 0把是否过期完全交给外部库决定避免双重缓存策略打架。手动预加载当预加载时机不由链接驱动如在某个组件副作用里预取下一步路由时可通过 router 实例手动触发import { useRouter } from tanstack/react-router function Component() { const router useRouter() useEffect(() { router.preloadRoute({ to: /posts/$postId, params: { postId: 1 } }) }, [router]) return div / }router.preloadRoute的类型是PreloadRouteFnrouter.ts与导航选项共用同一套to/params类型推导天然类型安全。导航拦截useBlocker 与 Block编辑类页面常需在用户离开时阻止导航表单脏数据保护。useBlocker接收shouldBlockFn决定是否拦截import { useBlocker } from tanstack/react-router import { useState } from react function EditForm() { const [formIsDirty, setFormIsDirty] useState(false) useBlocker({ shouldBlockFn: () { if (!formIsDirty) return false const shouldLeave confirm(Are you sure you want to leave?) return !shouldLeave }, }) return form{/* ... */}/form }自定义拦截 UIwithResolver默认的confirm弹窗体验有限。设置withResolver: true后useBlocker返回proceed、reset与status让你渲染自定义确认对话框并控制放行/取消import { useBlocker } from tanstack/react-router import { useState } from react function EditForm() { const [formIsDirty, setFormIsDirty] useState(false) const { proceed, reset, status } useBlocker({ shouldBlockFn: () formIsDirty, withResolver: true, }) return ( form{/* ... */}/form {status blocked ( div pAre you sure you want to leave?/p button onClick{proceed}Yes/button button onClick{reset}No/button /div )} / ) }beforeunload 单独控制浏览器关闭/刷新页面的beforeunload事件与客户端导航是两套机制可通过enableBeforeUnload单独开关useBlocker({ shouldBlockFn: () formIsDirty, enableBeforeUnload: formIsDirty, })底层拦截逻辑从源码看拦截发生在导航提交链路中router.ts 的navigate内部会先检查目标协议拦截javascript:、blob:、data:等危险协议随后除非ignoreBlocker为真遍历history._getBlockers()注册的所有 blocker逐个 await 其blockerFn一旦有 blocker 返回应阻止即中止导航。这也意味着router.navigate({ ..., ignoreBlocker: true })可以在特殊场景下绕过拦截器如强制跳转。选项复用linkOptions 与 createLinklinkOptions一处定义多处使用linkOptions对导航选项对象做急切eager类型检查——错误在定义处就暴露而不是在展开spread进Link/navigate时才发现import { linkOptions, Link, useNavigate, redirect, } from tanstack/react-router const dashboardLinkOptions linkOptions({ to: /dashboard, search: { search: }, }) // Use anywhere: Link, navigate, redirect function Nav() { const navigate useNavigate() return ( div Link {...dashboardLinkOptions}Dashboard/Link button onClick{() navigate(dashboardLinkOptions)}Go/button /div ) } // Also works in an array for navigation bars const navOptions linkOptions([ { to: /dashboard, label: Summary, activeOptions: { exact: true } }, { to: /dashboard/invoices, label: Invoices }, { to: /dashboard/users, label: Users }, ]) function NavBar() { return ( nav {navOptions.map((option) ( Link {...option} key{option.to} activeProps{{ className: font-bold }} {option.label} /Link ))} /nav ) }同一组选项可安全地用于Link、useNavigate乃至redirect是构建可维护导航配置尤其导航栏/菜单数组的首选工具。linkOptions与createLink的实现位于 packages/react-router/src/link.tsx。createLink把任意组件包装成类型安全链接当需要自定义链接渲染如按钮样式、带图标的acreateLink可将任意组件包装为拥有完整导航类型能力的链接组件import * as React from react import { createLink, LinkComponent } from tanstack/react-router interface BasicLinkProps extends React.AnchorHTMLAttributesHTMLAnchorElement {} const BasicLinkComponent React.forwardRefHTMLAnchorElement, BasicLinkProps( (props, ref) { return a ref{ref} {...props} classNameblock px-3 py-2 text-blue-700 / }, ) const CreatedLinkComponent createLink(BasicLinkComponent) export const CustomLink: LinkComponenttypeof BasicLinkComponent (props) { return CreatedLinkComponent preloadintent {...props} / }使用自定义链接时类型安全不打折CustomLink to/dashboard/invoices/$invoiceId params{{ invoiceId: 0 }} /从源码看createLinklink.tsx会注入内部导航逻辑并把isActive状态透传给被包装组件如通过data-status属性见第 855 行附近的读取逻辑因此自定义组件也能感知激活态。滚动恢复Scroll RestorationSPA 切换路由时浏览器不会自动恢复滚动位置TanStack Router 提供内建的滚动恢复机制。全局开启const router createRouter({ routeTree, scrollRestoration: true, })嵌套可滚动区域页面内存在独立滚动容器非 window 滚动时用scrollToTopSelectors指定这些容器切换路由时它们也会被正确处理const router createRouter({ routeTree, scrollRestoration: true, scrollToTopSelectors: [#main-scrollable-area], })该选项的类型支持string或返回元素的函数router.ts。自定义缓存键默认按上一个位置维度缓存滚动位置。getScrollRestorationKey可自定义缓存粒度例如只按 pathname 缓存忽略 search 变化const router createRouter({ routeTree, scrollRestoration: true, getScrollRestorationKey: (location) location.pathname, })其类型签名是(location: ParsedLocation) stringrouter.ts。按导航关闭滚动重置某些导航如加载更多后原地返回不想重置滚动位置可在Link上设置resetScroll{false}Link to/posts resetScroll{false} Posts /Link底层实现滚动恢复的实现位于 packages/router-core/src/scroll-restoration.ts以ScrollRestorationEntry { scrollX; scrollY }第 5 行记录 window 或指定元素的滚动坐标导航时通过scrollToTopSelectors找出所有目标元素getScrollToTopElements第 152 行附近在应当重置滚动时shouldResetScroll对新旧页面元素做坐标恢复element.scrollTop scrollY第 335 行并在没有 hash 时执行 window 级滚动重置第 305 行附近。这套逻辑同时兼容 window 滚动与嵌套容器的元素滚动。MatchRoute待定导航的加载态 UI导航进行中新路由还在加载 loader 数据时可用MatchRoute的pending模式渲染加载指示器import { Link, MatchRoute } from tanstack/react-router function Nav() { return ( Link to/users Users MatchRoute to/users pending Spinner / /MatchRoute /Link ) }MatchRoute在内部匹配当前路由状态pending表示导航到该路由但尚未完成时渲染子节点是打造感知流畅的导航反馈的标准手法。常见错误清单技能文档明确标注了三个等级的常见陷阱务必规避CRITICAL把参数插值进 to 字符串// WRONG — breaks type safety and param encoding Link to{/posts/${postId}}Post/Link // CORRECT — use the params option Link to/posts/$postId params{{ postId }}Post/Link路径动态段用$声明必须经params传入。字符串插值既破坏类型校验也无法保证参数的正确 URL 编码。该规则对Link、useNavigate、Navigate、router.navigate一视同仁。MEDIUM用 useNavigate 处理可点击元素// WRONG — no href, no cmdclick, no preloading, no accessibility function BadNav() { const navigate useNavigate() return button onClick{() navigate({ to: /posts })}Posts/button } // CORRECT — real a tag with href, accessible, preloadable function GoodNav() { return Link to/postsPosts/Link }useNavigate只应用于编程式副作用导航表单提交、异步回调等。可点击的导航入口一律使用Link以保留href中键/ctrl点击新开标签页、无障碍语义与预加载能力。HIGH相对导航不提供 from// WRONG — without from, .. resolves from root Link to..Back/Link // CORRECT — provide from for relative resolution Link from{Route.fullPath} to..Back/Link没有from..从根路径解析行为与预期相悖且只有绝对路径能获得类型自动补全与校验。HIGHsearch 传对象会丢失已有参数// WRONG — replaces ALL search params with just { page: 2 } Link to. search{{ page: 2 }}Page 2/Link // CORRECT — preserves existing search params, updates page Link to. search{(prev) ({ ...prev, page: 2 })}Page 2/Linksearch传普通对象会整体替换当前所有 search 参数需要保留并更新时必须使用函数形式(prev) ...展开旧值。search与参数校验的深层交互见 search-params 技能文档。扩展阅读router-core 主技能文档 —— 路由核心能力的总览type-safety 技能文档 ——from收窄类型推断的原理search-params 技能文档 ——search与校验适配器router-core 源码目录 ——link.ts导航类型、router.ts导航/拦截/预加载运行时、scroll-restoration.ts滚动恢复实现react-router 的 Link 实现 ——Link/createLink/linkOptions的 React 侧实现router-core 测试目录 —— 导航相关的测试用例含useBlocker、预加载、滚动恢复等场景【免费下载链接】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),仅供参考
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。