Skip to content
第 154 / 250 章前端⏱ 10 分钟阅读

第 154 章:React Router(Vite 版)

学习目标

  • 掌握 React Router 6+ 配置
  • 学会声明式路由与嵌套路由
  • 理解动态参数与查询参数
  • 实现导航守卫

一、为什么需要路由

单页应用(SPA)需要在前端模拟多页面切换。

二、安装

bash
pnpm add react-router-dom

Next.js 用文件系统路由,不需要这个库。本章针对 Vite + React 项目。

三、基础配置

3.1 main.tsx

tsx
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import App from './App';

createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </StrictMode>
);

3.2 路由表

tsx
// App.tsx
import { Routes, Route, Link } from 'react-router-dom';
import { Home } from './pages/Home';
import { About } from './pages/About';
import { User } from './pages/User';
import { NotFound } from './pages/NotFound';

function App() {
  return (
    <div>
      <nav>
        <Link to="/">首页</Link>
        <Link to="/about">关于</Link>
        <Link to="/user/123">用户</Link>
      </nav>

      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="/user/:id" element={<User />} />
        <Route path="*" element={<NotFound />} />
      </Routes>
    </div>
  );
}

四、动态参数

4.1 useParams

tsx
// pages/User.tsx
import { useParams } from 'react-router-dom';

export function User() {
  const { id } = useParams<{ id: string }>();
  return <h1>用户 ID:{id}</h1>;
}

4.2 useSearchParams(查询参数)

tsx
import { useSearchParams } from 'react-router-dom';

function Products() {
  const [params, setParams] = useSearchParams();
  const page = params.get('page') ?? '1';
  const sort = params.get('sort') ?? 'asc';

  return (
    <>
      <p>第 {page} 页,排序 {sort}</p>
      <button onClick={() => setParams({ page: '2' })}>第 2 页</button>
    </>
  );
}

五、嵌套路由

tsx
<Routes>
  <Route path="/" element={<Layout />}>
    <Route index element={<Home />} />
    <Route path="about" element={<About />} />
    <Route path="user" element={<UserLayout />}>
      <Route index element={<UserList />} />
      <Route path=":id" element={<UserDetail />} />
    </Route>
  </Route>
</Routes>
tsx
// Layout.tsx
import { Outlet, Link } from 'react-router-dom';

export function Layout() {
  return (
    <div>
      <nav>
        <Link to="/">首页</Link>
        <Link to="/about">关于</Link>
        <Link to="/user">用户</Link>
      </nav>
      <main>
        <Outlet />   {/* 子路由渲染位置 */}
      </main>
    </div>
  );
}

六、编程式导航

tsx
import { useNavigate } from 'react-router-dom';

function LoginForm() {
  const navigate = useNavigate();

  async function handleLogin() {
    await login();
    navigate('/dashboard');          // 跳转
    navigate(-1);                    // 后退
    navigate('/detail', { replace: true });  // 替换(不留历史)
    navigate('/detail', { state: { from: 'login' } });  // 传 state
  }
}
tsx
import { NavLink } from 'react-router-dom';

<NavLink
  to="/about"
  className={({ isActive }) => isActive ? 'text-blue-500' : 'text-gray-500'}
>
  关于
</NavLink>

八、路由守卫(鉴权)

tsx
function RequireAuth({ children }: { children: ReactNode }) {
  const { user } = useAuth();
  const location = useLocation();

  if (!user) {
    return <Navigate to="/login" state={{ from: location }} replace />;
  }
  return <>{children}</>;
}

// 使用
<Route path="/dashboard" element={
  <RequireAuth><Dashboard /></RequireAuth>
} />

九、懒加载

tsx
import { lazy, Suspense } from 'react';

const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));

function App() {
  return (
    <Suspense fallback={<Spinner />}>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
      </Routes>
    </Suspense>
  );
}

十、错误边界

tsx
import { useRouteError, isRouteErrorResponse } from 'react-router-dom';

function ErrorPage() {
  const error = useRouteError();

  if (isRouteErrorResponse(error)) {
    return <h1>{error.status} - {error.statusText}</h1>;
  }

  return <h1>未知错误</h1>;
}

// 在路由中
<Route path="*" element={<ErrorPage />} />

十一、Next.js vs Vite Router

维度Vite + React RouterNext.js App Router
配置手动文件系统
SSR
数据获取useEffect + fetchServer Components
学习成本
适合SPA / 后台SEO 站 / 全栈

十二、本章小结

概念关键
BrowserRouter顶层包裹
Routes/Route路由配置
useParams动态参数
useSearchParams查询参数
useNavigate编程式导航
NavLink高亮
懒加载lazy + Suspense

动手练习

  1. 配置 4 个路由:首页、关于、用户列表、用户详情
  2. 用 useParams 接收用户 ID
  3. 用 useSearchParams 实现分页
  4. 实现 RequireAuth 守卫
  5. 路由用 lazy 拆分

推荐阅读


下一章:第 155 章:状态管理 Zustand

本站基于 VitePress 构建 · 由 Codebook 团队维护