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 即状态,完美持久化 |
建议步骤:
- 先保持你的
App.tsx不变,尝试引入createRouter并定义一个最简单的根路由。 - 尝试将
Safework路由迁移过去。 - 慢慢把你组件里的
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: 对应路径/safeworkonsite.index.tsx: 对应路径/onsiteonsite.$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
浙公网安备 33010602011771号