Tanstack Router

Tanstack Router

使用如下命令安装 @tanstack/react-router 而不是 @tanstack/router,那个是早期版本的

pnpm install @tanstack/react-router
"@tanstack/router-generator": "^1.166.39",
    "@tanstack/router-plugin": "^1.167.35",

定义路由结构

React Router 喜欢在 JSX 里写 <Route>
而 TanStack Router 倾向于先定义路由对象 Route

创建根路由 src/routes/__root.tsx

import { createRootRoute, Outlet } from '@tanstack/react-router'
import { TanStackRouterDevtools } from '@tanstack/router-devtools'

export const Route = createRootRoute({
  component: () => (
    <>
      <Outlet /> 
      <TanStackRouterDevtools />
    </>
  ),
})

创建具体路由 src/routes/safework.tsx

import { createRoute } from '@tanstack/react-router'
import { Route as rootRoute } from './__root'
import Safework from '@/pages/Safework'

export const safeworkRoute = createRoute({
  getParentRoute: () => rootRoute,
  path: '/safework',
  component: Safework,
})

3. 初始化 Router 并替换 App.tsx

现在我们将这些路由组装起来,替换掉你原来的 BrowserRouter

import { Router, RouterProvider, createRouter } from '@tanstack/react-router'
import { Route as rootRoute } from './routes/__root'
import { safeworkRoute } from './routes/safework'
// ... 导入其他路由

// 1. 创建路由树
const routeTree = rootRoute.addChildren([safeworkRoute, /* 其他路由 */])

// 2. 创建 router 实例
const router = createRouter({ routeTree })

// 3. 注册类型(这是最爽的一步,后续跳转会有自动补全)
declare module '@tanstack/react-router' {
  interface Register {
    router: typeof router
  }
}

export function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <AuthProvider>
        {/* 替换掉原来的 BrowserRouter */}
        <RouterProvider router={router} />
      </AuthProvider>
    </QueryClientProvider>
  )
}

4. 为什么要切?—— 看看“搜索参数”的威力

你之前的 Safework 页面有很多状态(searchBegin, searchEnd, pageIndex)。在 React Router 中,如果你刷新页面,这些状态就丢了。

在 TanStack Router 中,你可以强制要求这些参数必须存在,并且是类型安全的。

在路由中定义搜索参数模式:

// src/routes/safework.tsx
import { z } from 'zod' // 推荐配合 zod 使用

const safeworkSearchSchema = z.object({
  pageIndex: z.number().catch(0),
  pageSize: z.number().catch(10),
  searchJobContent: z.string().optional(),
})

export const safeworkRoute = createRoute({
  getParentRoute: () => rootRoute,
  path: '/safework',
  validateSearch: (search) => safeworkSearchSchema.parse(search), // 验证参数
  component: Safework,
})

在组件中使用参数:

export function Safework() {
  // 自动获得类型补全的搜索参数!
  const { pageIndex, pageSize, searchJobContent } = safeworkRoute.useSearch()

  // 跳转时也会强制要求你传入正确的参数
  const navigate = useNavigate()
  
  // 如果你漏写了参数,TypeScript 会直接报错
  const goToNextPage = () => navigate({ 
    to: '/safework', 
    search: (prev) => ({ ...prev, pageIndex: prev.pageIndex + 1 }) 
  })
}

5. 解决你之前的“Token 延迟”问题:Loader 模式

还记得你担心的“页面启动时没有 Token 无法请求数据”吗?TanStack Router 有一个内置的 loader 功能。

它允许你在进入路由之前就去尝试获取数据。如果 AuthProvider 还没准备好,你可以让路由等待。

export const safeworkRoute = createRoute({
  // ...
  loader: async () => {
    // 你可以在这里检查 TokenManager.getAccessToken()
    // 如果没拿到,可以在这里抛出重定向,或者等待登录完成
  }
})

总结:迁移对比表

特性 React Router (原有) TanStack Router (目标)
路由定义 JSX 组件声明 集中对象配置
类型安全 弱(跳转时路径写错不会报错) 极强(路径和参数均有补全)
搜索参数 需手动解析、手动拼字符串 原生支持校验与同步
数据同步 需在组件内用 useEffect 支持 loader 预加载
状态持久化 刷新页面后 Search Params 易丢失 URL 即状态,完美持久化

建议步骤:

  1. 先保持你的 App.tsx 不变,尝试引入 createRouter 并定义一个最简单的根路由。
  2. 尝试将 Safework 路由迁移过去。
  3. 慢慢把你组件里的 useState(关于分页和搜索条件的)迁移到路由的 useSearch 中。

当你敲下 Maps({ to: '...' }) 发现 IDE 会自动提示你有哪些路径时,你就会发现这个迁移是非常值得的。你想先从哪个具体的页面开始尝试路由定义?

Tanstack Router Plugin

安装了 @tanstack/router-plugin 之后 vite 会根据你的文件目录结构自动生成的,形成所谓的“文件即路由”的模式

pnpm install -D @tanstack/router-plugin

配置 vite.config.ts

在 Vite 的插件列表中加入 tanstackRouter。它会自动扫描你的 routes 目录并实时生成路由树。

import tailwindcss from "@tailwindcss/vite"
import react from "@vitejs/plugin-react"
import path from "path"
import { defineConfig } from "vite"
import { tanstackRouter } from '@tanstack/router-plugin/vite'

export default defineConfig({
  plugins: [
    tanstackRouter({
      target: 'react',
      autoCodeSplitting: true,
    }),
    react(), 
    tailwindcss(),
  ],
  resolve: {
    alias: {
      "@": path.resolve(__dirname, "./src"),
    },
  },
  server: {
    host: '0.0.0.0'
  }
})

配置完成后,pnpm run dev 启动项目,插件会自动检测到 src/routes 的变化

如果你看到控制台提示 File-based routing tree generated,那么 src/routeTree.gen.ts 就会出现在你的目录中

File-based Routing(基于文件的路由)

src/routes 目录下,文件命名决定了路由路径:

  • __root.tsx: 根布局(所有路由的父级)
  • index.tsx: 对应路径 /
  • safework.tsx: 对应路径 /safework
  • onsite.index.tsx: 对应路径 /onsite
  • onsite.$id.tsx: 动态路由,对应 /onsite/123

App.tsx

import { RouterProvider, createRouter } from '@tanstack/react-router'
import { routeTree } from './routeTree.gen.ts'

const router = createRouter({ routeTree })

declare module '@tanstack/react-router' {
  interface Register {
    router: typeof router
  }
}

export function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <AuthProvider>
        <RouterProvider router={router} />
      </AuthProvider>
    </QueryClientProvider>
  )
}

参考文档

https://tanstack.com/router/latest/docs/installation/with-vite

posted @ 2026-05-10 19:14  tommao9925  阅读(11)  评论(0)    收藏  举报