--- tags: [React, Router, Navigation, Frontend] create time: 2026-04-29 22:07 --- # 路由管理 ## 概述 前端路由是单页应用(SPA)的核心基础设施。本文档以 React Router v7 为主介绍路由配置、嵌套路由、懒加载、动态路由和守卫拦截等实战模式。 ## React Router v7 核心概念 ```mermaid graph TB A[BrowserRouter] --> B["哈希历史 vs HTML5 历史"] B --> C[RouterProvider] C --> D[Route 树] D --> E["Element / Component / ComponentFunction"] D --> F["Layout Route"] F --> G["Child Routes"] style A fill:#F4DBD6,color:#000 style E fill:#61DAFB,color:#000 ``` ### 基本结构 ```tsx // app.tsx(v7 推荐入口) import { createRootRouteWithContext, createRouter, RouterProvider } from "@tanstack/react-router"; // 或传统 react-router-dom v6/v7 import { createBrowserRouter, RouterProvider } from "react-router-dom"; const router = createBrowserRouter([ { path: "/", element: , children: [ { index: true, element: }, { path: "about", element: }, ], }, ]); function App() { return ; } ``` ## Layout Route 与嵌套路由 ```tsx { // /dashboard 及其子路径共用此布局 path: "dashboard", element: , // 侧边栏 + Header 常驻 children: [ { index: true, element: }, // /dashboard → DashboardHome { path: "stats", element: }, // /dashboard/stats { path: "settings", element: }, // /dashboard/settings { path: "settings/:tab", element: }, // /dashboard/settings/profile ], } ``` ```jsx // DashboardLayout.tsx function DashboardLayout() { return (
{/* v7 Outlet 替代了 v6的children渲染 */}
); } ``` > [!note] Outlet vs Children > - v6 用 `` 内部定义子路由 > - v7 用 `` 显式标注嵌套出口,更清晰 ## 路由参数获取 ```tsx import { useSearchParams, useParams, useNavigate, useLocation } from "react-router-dom"; function UserPage() { const { id } = useParams(); // /user/:id → { id: "123" } const [searchParams] = useSearchParams(); // ?q=hello&page=1 const location = useLocation(); // { pathname: "/user/123", search: "?q=hello" } const navigate = useNavigate(); // navigate("/home") / navigate(-1) const query = searchParams.get("q"); return

User #{id}, searching for "{query}"

; } ``` ### v7 新增:useRouteContext + Data APIs ```tsx // v7 data routers 支持 loader/action { path: "posts/:postId", loader: async ({ params }) => { const res = await fetch(`/api/posts/${params.postId}`); return res.json(); }, // loader 数据自动注入 component props } ``` ## 懒加载 Code Splitting ```tsx import { lazy, Suspense } from "react"; // 路由级代码分割 const AdminPage = lazy(() => import("./pages/AdminPage")); const SettingsPage = lazy(() => import("./pages/SettingsPage")); { path: "admin", element: ( }> ), } ``` > [!tip] 懒加载时机判断 > - 首屏路由(首页、登录页)**不要**懒加载 > - 低频访问页面(设置、管理员面板)适合懒加载 > - 每个 chunk 建议不超过 100KB gzipped ## 动态路由与 Splat 路由 ```tsx // :param —— 单个段匹配 { path: "users/:userId", element: } // /users/42 // * splat —— 贪婪匹配剩余所有 { path: "docs/*", element: } // /docs/a/b/c { path: "*", element: } // 404 fallback ``` ## 路由守卫与权限控制 ```tsx // 方案1:条件渲染(简单场景) function PrivateRoute({ children }: { children: React.ReactNode }) { const { user } = useAuth(); if (!user) return ; return <>{children}; } // 方案2:高阶包装 const withAuth = (Component: React.FC) => { return (props: any) => { const { loading, authenticated } = useAuth(); if (loading) return ; if (!authenticated) return ; return ; }; }; // 方案3:v7 Loader 守卫(服务端前置检查) { path: "admin", loader: () => { if (!isAuthenticated()) throw new Response("", { status: 401 }); if (!isAdmin()) throw new Response("", { status: 403 }); }, element: , } ``` ## 导航 API 对比 | 方法 | 适用场景 | 是否保留历史记录 | |------|----------|------------------| | `navigate(path)` | 程序化跳转 | ✅ 有 history entry | | `Home` | 声明式导航 | ✅ 预加载 prefetch | | `navigate(-1)` | 返回上一页 | ✅ | | `` | 替换当前 entry | ❌ 不增加 history | ## 关联笔记