Back to Cherry Studio

路由系统开发指南

src/renderer/routes/README.zh-CN.md

2.0.06.0 KB
Original Source

路由系统开发指南

本项目使用 TanStack Router + Multi MemoryRouter 架构,每个 Tab 拥有独立的路由实例,实现原生 KeepAlive。

快速开始

1. 添加新页面

src/renderer/routes/ 目录下创建文件:

typescript
// routes/knowledge.tsx
import { createFileRoute } from '@tanstack/react-router'

export const Route = createFileRoute('/knowledge')({
  component: KnowledgePage
})

function KnowledgePage() {
  return <div>Knowledge Page</div>
}

运行 yarn dev 后,TanStack Router 会自动更新 routeTree.gen.ts

2. 带参数的路由

typescript
// routes/chat/$topicId.tsx
import { createFileRoute } from '@tanstack/react-router'

export const Route = createFileRoute('/chat/$topicId')({
  component: ChatPage
})

function ChatPage() {
  const { topicId } = Route.useParams()
  return <div>Chat: {topicId}</div>
}

3. 嵌套路由

text
routes/
├── settings.tsx        # /settings (布局)
├── settings/
│   ├── general.tsx     # /settings/general
│   └── provider.tsx    # /settings/provider
typescript
// routes/settings.tsx
import { createFileRoute, Outlet } from '@tanstack/react-router'

export const Route = createFileRoute('/settings')({
  component: SettingsLayout
})

function SettingsLayout() {
  return (
    <div className="flex">
      <aside>Settings Menu</aside>
      <main><Outlet /></main>
    </div>
  )
}

导航 API

本项目有两种导航方式:

1. Tab 级别导航 - openTab

打开新 Tab 或切换到已有 Tab,使用 useTabs hook:

typescript
import { useTabs } from '@renderer/hooks/tab'

function MyComponent() {
  const { openTab, closeTab } = useTabs()

  // 基础用法 - 复用已有 Tab 或新建
  openTab('/settings')

  // 带标题
  openTab('/chat/123', { title: 'Chat with Alice' })

  // 强制新开 Tab(即使已有相同 URL)
  openTab('/settings', { forceNew: true })

  // 打开 Webview Tab
  openTab('https://example.com', {
    type: 'webview',
    title: 'Example Site'
  })

  // 关闭 Tab
  closeTab(tabId)
}

2. Tab 内部导航 - useNavigate

在同一个 Tab 内跳转路由(不会新开 Tab),使用 TanStack Router 的 useNavigate

typescript
import { useNavigate } from '@tanstack/react-router'

function SettingsPage() {
  const navigate = useNavigate()

  // 在当前 Tab 内跳转到子页面
  navigate({ to: '/settings/provider' })

  // 带参数跳转
  navigate({ to: '/chat/$topicId', params: { topicId: '123' } })
}

两者区别

场景使用效果
打开新功能模块openTab('/knowledge')新建 Tab
设置页内切换子页navigate({ to: '/settings/provider' })当前 Tab 内跳转
从列表打开详情openTab('/chat/123', { title: '...' })新建 Tab
返回上一页navigate({ to: '..' })当前 Tab 内返回

API 参考

useTabs() 返回值

属性/方法类型说明
tabsTab[]所有 Tab 列表
activeTabIdstring当前激活的 Tab ID
activeTabTab | undefined当前激活的 Tab 对象
openTab(url, options?)(url: string, options?: OpenTabOptions) => string打开 Tab,返回 Tab ID
closeTab(id)(id: string) => void关闭指定 Tab
setActiveTab(id)(id: string) => void切换到指定 Tab
updateTab(id, updates)(id: string, updates: Partial<Tab>) => void更新 Tab 属性

OpenTabOptions

选项类型默认值说明
forceNewbooleanfalse强制新开 Tab
titlestringURL 路径Tab 标题
type'route' | 'webview''route'Tab 类型
idstring自动生成自定义 Tab ID

架构说明

text
AppShell
├── Sidebar
├── TabBar
└── Content Area
    ├── TabRouter #1 (Home)
    │   └── Activity(visible) → MemoryRouter → RouterProvider
    ├── TabRouter #2 (Settings)
    │   └── Activity(hidden) → MemoryRouter → RouterProvider
    └── WebviewContainer (for webview tabs)
  • 每个 Tab 拥有独立的 MemoryRouter 实例
  • 使用 React 19 <Activity> 组件控制可见性
  • Tab 切换时组件不卸载,状态完全保持(KeepAlive)

错误处理

层级机制作用范围
路由 render 错误每 tab router 的 defaultErrorComponent: RouteErrorFallbackTabRouter.tsx圈禁在抛错 tab 内;带主题的错误卡片,可重试/重载
Provider render 错误各窗口 App 最外层 <ErrorBoundary fallbackComponent={WindowFatalFallback}>整窗回退到 context-free 致命错误页,不再白屏
  • 单个路由可用自己的 errorComponent 路由选项覆盖默认
  • 若无 defaultErrorComponent,TanStack 以透传 fragment 包裹 match:路由 render 错误会冒泡到窗口级边界,炸掉整窗

文件结构

text
src/renderer/
├── routes/                    # 路由页面(TanStack Router 文件路由)
│   ├── __root.tsx            # 根路由(渲染 Outlet)
│   ├── settings.tsx          # /settings
│   ├── settings.index.tsx    # /settings/ 索引路由(平铺点记法——禁止裸 index.tsx)
│   └── README.md             # 本文档
├── components/layout/
│   ├── AppShell.tsx          # 主布局(Sidebar + TabBar + Content)
│   └── TabRouter.tsx         # Tab 路由容器(MemoryRouter + Activity)
├── hooks/
│   └── useTabs.ts            # Tab 状态管理 Hook
└── routeTree.gen.ts          # 自动生成的路由树(勿手动编辑)

注意事项

  1. 不要手动编辑 routeTree.gen.ts - 它由 TanStack Router 自动生成
  2. 路由文件命名即路径 - routes/settings.tsx/settings
  3. 动态参数使用 $ - routes/chat/$topicId.tsx/chat/:topicId
  4. 页面状态自动保持 - Tab 切换不会丢失 useState、滚动位置等