# 使用 TanStack Router 实现 SSR > 在 Nitro 中使用 Vite 结合 TanStack Router 实现客户端路由。 ```html [index.html] Nitro + TanStack Router + React
``` ```json [package.json] { "type": "module", "scripts": { "build": "vite build", "dev": "vite dev", "preview": "vite preview" }, "devDependencies": { "@tanstack/react-router": "^1.158.1", "@tanstack/react-router-devtools": "^1.158.1", "@tanstack/router-plugin": "^1.158.1", "@types/react": "^19.2.13", "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^5.1.3", "nitro": "latest", "react": "^19.2.4", "react-dom": "^19.2.4", "vite": "beta" } } ``` ```json [tsconfig.json] { "extends": "nitro/tsconfig", "compilerOptions": { "baseUrl": ".", "jsx": "react-jsx", "paths": { "@/*": ["sec/*"] } } } ``` ```js [vite.config.mjs] import { defineConfig } from "vite"; import { nitro } from "nitro/vite"; import react from "@vitejs/plugin-react"; import { tanstackRouter } from "@tanstack/router-plugin/vite"; export default defineConfig({ plugins: [tanstackRouter({ target: "react", autoCodeSplitting: true }), react(), nitro()], }); ``` ```tsx [src/main.tsx] import { StrictMode } from "react"; import ReactDOM from "react-dom/client"; import { RouterProvider, createRouter } from "@tanstack/react-router"; // Import the generated route tree import { routeTree } from "./routeTree.gen.ts"; // Create a new router instance const router = createRouter({ routeTree }); // Register the router instance for type safety declare module "@tanstack/react-router" { interface Register { router: typeof router; } } // Render the app const rootElement = document.querySelector("#root")!; if (!rootElement.innerHTML) { const root = ReactDOM.createRoot(rootElement); root.render( ); } ``` ```ts [src/routeTree.gen.ts] /* eslint-disable */ // @ts-nocheck // noinspection JSUnusedGlobalSymbols // This file was automatically generated by TanStack Router. // You should NOT make any changes in this file as it will be overwritten. // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. import { Route as rootRouteImport } from './routes/__root' import { Route as IndexRouteImport } from './routes/index' const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute } export interface FileRoutesByTo { '/': typeof IndexRoute } export interface FileRoutesById { __root__: typeof rootRouteImport '/': typeof IndexRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath fullPaths: '/' fileRoutesByTo: FileRoutesByTo to: '/' id: '__root__' | '/' fileRoutesById: FileRoutesById } export interface RootRouteChildren { IndexRoute: typeof IndexRoute } declare module '@tanstack/react-router' { interface FileRoutesByPath { '/': { id: '/' path: '/' fullPath: '/' preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } } } const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, } export const routeTree = rootRouteImport ._addFileChildren(rootRouteChildren) ._addFileTypes() ``` ```css [src/assets/main.css] :root { font-family: system-ui, Avenir, Helvetica, Arial, sans-serif; line-height: 1.5; font-weight: 400; color-scheme: light dark; color: rgba(255, 255, 255, 0.87); background-color: #242424; font-synthesis: none; text-rendering: optimizeLegibility; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } a { font-weight: 500; color: #ff2056; text-decoration: inherit; } a:hover { color: #ff637e; } body { margin: 0; display: flex; flex-direction: column; place-items: center; justify-content: center; min-width: 320px; min-height: 100vh; } h1 { font-size: 3.2em; line-height: 1.1; } #app { max-width: 1280px; margin: 0 auto; padding: 2rem; text-align: center; } .logo { height: 6em; padding: 1.5em; will-change: filter; transition: filter 300ms; transition: transform 300ms; } .logo:hover { transform: scale(1.1); } .card { padding: 2em; } .read-the-docs { color: #888; } button { border-radius: 8px; border: 1px solid transparent; padding: 0.6em 1.2em; font-size: 1em; font-weight: 500; font-family: inherit; background-color: #1a1a1a; cursor: pointer; transition: border-color 0.25s; } button:hover { border-color: #646cff; } button:focus, button:focus-visible { outline: 4px auto -webkit-focus-ring-color; } @media (prefers-color-scheme: light) { :root { color: #213547; background-color: #ffffff; } a:hover { color: #747bff; } button { background-color: #f9f9f9; } } ``` ```tsx [src/routes/__root.tsx] import { createRootRoute, Link, Outlet } from "@tanstack/react-router"; import { TanStackRouterDevtools } from "@tanstack/react-router-devtools"; const RootLayout = () => ( <>
Home

); export const Route = createRootRoute({ component: RootLayout }); ``` ```tsx [src/routes/index.tsx] import { createFileRoute } from "@tanstack/react-router"; export const Route = createFileRoute("/")({ loader: async () => { const r = await fetch("/api/hello"); return r.json(); }, component: Index, }); function Index() { const r = Route.useLoaderData(); return (

{JSON.stringify(r)}

); } ```
设置 TanStack Router 与 React、Vite 和 Nitro。该设置提供基于文件的路由,具有类型安全的导航和自动代码拆分。 ## 概览 #### 在 Vite 配置中添加 Nitro Vite 插件 #### 创建包含应用入口的 HTML 模板 #### 创建初始化路由的主入口文件 #### 使用基于文件的路由定义路由 ## 1. 配置 Vite 在 Vite 配置中添加 Nitro、React 和 TanStack Router 插件: ```js [vite.config.mjs] import { defineConfig } from "vite"; import { nitro } from "nitro/vite"; import react from "@vitejs/plugin-react"; import { tanstackRouter } from "@tanstack/router-plugin/vite"; export default defineConfig({ plugins: [tanstackRouter({ target: "react", autoCodeSplitting: true }), react(), nitro()], }); ``` `tanstackRouter` 插件根据你的 `routes/` 目录结构生成路由树。启用 `autoCodeSplitting` 以自动将路由拆分成独立代码块。请将 TanStack Router 插件放在 React 插件之前。 ## 2. 创建 HTML 模板 创建一个作为应用外壳的 HTML 文件: ```html [index.html] Nitro + TanStack Router + React
``` ## 3. 创建应用入口 创建初始化 TanStack Router 的主入口文件: ```tsx [src/main.tsx] import { StrictMode } from "react"; import ReactDOM from "react-dom/client"; 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; } } // 渲染应用 const rootElement = document.querySelector("#root")!; if (!rootElement.innerHTML) { const root = ReactDOM.createRoot(rootElement); root.render( ); } ``` `routeTree.gen.ts` 文件根据 `routes/` 目录结构自动生成。`Register` 接口声明提供了关于路由路径和参数的完整类型推断。`!rootElement.innerHTML` 检查防止热模块替换时的重复渲染。 ## 4. 创建根路由 根路由(`__root.tsx`)定义应用的布局: ```tsx [src/routes/__root.tsx] import { createRootRoute, Link, Outlet } from "@tanstack/react-router"; import { TanStackRouterDevtools } from "@tanstack/react-router-devtools"; const RootLayout = () => ( <>
首页

); export const Route = createRootRoute({ component: RootLayout }); ``` 使用 `Link` 实现类型安全的导航,并带有激活状态样式。`Outlet` 组件渲染子路由。包含 `TanStackRouterDevtools` 用于开发调试(生产环境会自动移除)。 ## 5. 创建页面路由 页面路由使用 `createFileRoute`,可以包含加载器: ```tsx [src/routes/index.tsx] import { createFileRoute } from "@tanstack/react-router"; export const Route = createFileRoute("/")({ loader: async () => { const r = await fetch("/api/hello"); return r.json(); }, component: Index, }); function Index() { const r = Route.useLoaderData(); return (

{JSON.stringify(r)}

); } ``` 使用 `loader` 函数在渲染前获取数据——数据通过 `Route.useLoaderData()` 可用。文件路径对应 URL 路径:`routes/index.tsx` 映射为 `/`,`routes/about.tsx` 映射为 `/about`,`routes/users/$id.tsx` 映射为 `/users/:id`。 ## 更多学习资源 - [TanStack Router 文档](https://tanstack.com/router) - [Renderer](/docs/renderer)