一、安装与基本结构
1
| npm install react-router-dom
|
React Router 6 推荐的 API 是 createBrowserRouter(数据路由),路由配置集中管理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import { createRoot } from 'react-dom/client' import { RouterProvider, createBrowserRouter } from 'react-router-dom' import Layout from './Layout' import Home from './pages/Home' import About from './pages/About' import NotFound from './pages/NotFound'
const router = createBrowserRouter([ { path: '/', element: <Layout />, children: [ { index: true, element: <Home /> }, { path: 'about', element: <About /> }, { path: '*', element: <NotFound /> } ] } ])
createRoot(document.getElementById('root')).render( <RouterProvider router={router} /> )
|
关键点:
- 嵌套路由:
Layout 作为父路由,用 <Outlet /> 渲染子路由;
index: true:父路径的默认子路由(访问 / 时渲染 Home);
*:通配符兜底 404。
二、Layout 与 Outlet
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import { Outlet, NavLink } from 'react-router-dom'
export default function Layout() { return ( <div> <nav> {/* NavLink 自动处理激活态 */} <NavLink to="/" className={({ isActive }) => (isActive ? 'active' : '')} end // end 让首页只在完全匹配时高亮 > 首页 </NavLink> <NavLink to="/about">关于</NavLink> </nav>
{/* 子路由渲染的位置 */} <Outlet /> </div> ) }
|
三、编程式导航与传参
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import { useNavigate, useParams, useSearchParams } from 'react-router-dom'
function UserDetail() { const navigate = useNavigate() const { id } = useParams() const [searchParams, setSearchParams] = useSearchParams()
const page = searchParams.get('page') || '1'
return ( <div> <p>用户:{id},页码:{page}</p> <button onClick={() => setSearchParams({ page: '3' })}>下一页</button> <button onClick={() => navigate('/')}>回首页</button> </div> ) }
|
四、路由守卫:未登录跳登录页
数据路由里可以用 loader + 重定向做守卫:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import { redirect } from 'react-router-dom'
function requireAuth() { const token = localStorage.getItem('token') if (!token) { return redirect(`/login?redirect=${encodeURIComponent(location.pathname)}`) } return null }
const router = createBrowserRouter([ { path: '/dashboard', loader: requireAuth, element: <Dashboard /> } ])
|
或者更直观的组件式守卫:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| function RequireAuth({ children }) { const token = localStorage.getItem('token') const location = useLocation()
if (!token) { return <Navigate to="/login" replace state={{ from: location }} /> } return children }
<Route path="/dashboard" element={ <RequireAuth> <Dashboard /> </RequireAuth> } />
|
五、懒加载与代码分割
React.lazy + Suspense 让路由级组件按需加载,首屏只下载必要代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| import { Suspense, lazy } from 'react' import { RouterProvider, createBrowserRouter } from 'react-router-dom'
const Home = lazy(() => import('./pages/Home')) const About = lazy(() => import('./pages/About')) const Dashboard = lazy(() => import('./pages/Dashboard'))
const router = createBrowserRouter([ { path: '/', element: <Layout />, children: [ { index: true, element: <Home /> }, { path: 'about', element: <About /> }, { path: 'dashboard', element: <Dashboard /> } ] } ])
function App() { return ( <Suspense fallback={<div>页面加载中...</div>}> <RouterProvider router={router} /> </Suspense> ) }
|
构建后看产物:每个路由页面对应一个独立 chunk,首屏只加载入口 + 首页。
六、错误边界:路由级容错
数据路由支持配置 errorElement,路由渲染或 loader 出错时展示错误页:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| function ErrorPage() { const error = useRouteError() return ( <div> <h1>出错了</h1> <p>{error.status === 404 ? '页面不存在' : '加载失败,请刷新重试'}</p> </div> ) }
const router = createBrowserRouter([ { path: '/', element: <Layout />, errorElement: <ErrorPage />, children: [] } ])
|
七、部署注意:SPA 刷新 404
React Router 用 history 模式,服务器必须把所有路径回退到 index.html:
1 2 3 4
| location / { try_files $uri $uri/ /index.html; }
|
不然直接访问 /about 会 404。
常见坑
- NavLink 高亮错乱:忘记
end 属性,首页在任意路径都高亮;
useParams 取不到值:确认路由路径写的是 :id 且当前路由确实匹配;
- 懒加载白屏:Suspense 没包住懒组件,或组件内部抛错,配合 errorElement 排查;
- 跳转后滚动位置不对:用
useScrollRestoration(数据路由内置)或自己监听。
总结
React Router 6 的实践核心:createBrowserRouter 集中配置 → 嵌套路由 + Outlet → 守卫(loader/组件式)→ lazy 分包 → errorElement 容错。这套组合能支撑中大型前端应用的导航和权限体系。