说说你对 React Router 的理解?常用的 Router 组件有哪些?

一、是什么

React Router 是 React 生态中最主流的路由解决方案,用于在单页应用(SPA)中实现客户端路由。它让 URL 与 UI 保持同步,使用户可以通过浏览器地址栏导航到不同的页面视图,而不需要向服务器发起完整的页面请求。

从 v6 版本开始,React Router 引入了更简洁的 API 和基于数据的路由模式,v6.4+ 更是引入了 loader/action 等数据获取能力。

URL 变化 → Router 匹配 → 渲染对应组件 → UI 更新

二、核心组件

BrowserRouter — 路由容器

BrowserRouter 是最常用的顶层路由容器,基于 HTML5 History API 实现。它为整个应用提供路由上下文:

import { BrowserRouter } from 'react-router-dom';

function App() {
  return (
    <BrowserRouter>
      <MainLayout />
    </BrowserRouter>
  );
}

Routes 与 Route — 路由声明

Routes 替代了 v5 中的 SwitchRoute 用于声明路径与组件的映射关系:

import { Routes, Route } from 'react-router-dom';

function MainLayout() {
  return (
    <Routes>
      <Route path="/" element={<Home />} />
      <Route path="/about" element={<About />} />
      <Route path="/users/:id" element={<UserDetail />} />
      <Route path="*" element={<NotFound />} />
    </Routes>
  );
}

v6 中 Routeelement 属性接收 JSX 元素而非组件引用,path="*" 用于匹配所有未定义的路由(404 页面)。

Link 渲染为 <a> 标签,阻止默认浏览器导航行为,改为客户端路由跳转:

import { Link, NavLink } from 'react-router-dom';

function Navigation() {
  return (
    <nav>
      <Link to="/">首页</Link>
      <Link to="/about">关于</Link>

      {/* NavLink 可根据激活状态应用样式 */}
      <NavLink
        to="/dashboard"
        className={({ isActive, isPending }) =>
          isActive ? 'nav-active' : isPending ? 'nav-pending' : ''
        }
      >
        控制台
      </NavLink>
    </nav>
  );
}

NavLinkLink 的增强版本,提供 isActiveisPending 状态,适用于导航栏高亮场景。

Navigate 用于声明式重定向,替代了 v5 的 Redirect

import { Navigate } from 'react-router-dom';

function ProtectedRoute({ children }: { children: React.ReactNode }) {
  const { isAuthenticated } = useAuth();

  if (!isAuthenticated) {
    return <Navigate to="/login" replace />;
  }

  return <>{children}</>;
}

replace 属性表示替换当前历史记录条目而非新增。

Outlet — 嵌套路由出口

Outlet 类似于 Vue 中的 <router-view>,用于渲染嵌套子路由对应的组件:

function DashboardLayout() {
  return (
    <div className="dashboard">
      <Sidebar />
      <main>
        <Outlet />
      </main>
    </div>
  );
}

function AppRoutes() {
  return (
    <Routes>
      <Route path="/dashboard" element={<DashboardLayout />}>
        <Route index element={<DashboardHome />} />
        <Route path="settings" element={<Settings />} />
        <Route path="profile" element={<Profile />} />
      </Route>
    </Routes>
  );
}

访问 /dashboard 时渲染 DashboardHome,访问 /dashboard/settingsOutlet 会渲染 Settings 组件。

三、嵌套路由与布局路由

嵌套路由是 v6 的核心特性,允许路由配置体现页面的层级结构:

function AppRoutes() {
  return (
    <Routes>
      <Route element={<RootLayout />}>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />

        {/* 布局路由:不带 path,仅用于包裹子路由 */}
        <Route element={<AuthLayout />}>
          <Route path="/login" element={<Login />} />
          <Route path="/register" element={<Register />} />
        </Route>

        <Route path="/dashboard" element={<DashboardLayout />}>
          <Route index element={<Overview />} />
          <Route path="analytics" element={<Analytics />} />
          <Route path="users" element={<UserManagement />}>
            <Route index element={<UserList />} />
            <Route path=":userId" element={<UserDetail />} />
          </Route>
        </Route>
      </Route>
    </Routes>
  );
}

布局路由(Layout Route)没有 path 属性,仅提供共享的 UI 包裹层。当子路由匹配时,布局路由的 Outlet 处会渲染对应组件。

四、常用 Hooks

useParams — 获取动态路由参数

import { useParams } from 'react-router-dom';

function UserDetail() {
  const { userId } = useParams<{ userId: string }>();

  return <div>用户 ID:{userId}</div>;
}

useNavigate — 编程式导航

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

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

  const handleLogin = async (formData: FormData) => {
    await login(formData);
    navigate('/dashboard', { replace: true });
  };

  const goBack = () => navigate(-1);

  return (
    <form onSubmit={handleSubmit(handleLogin)}>
      <button type="submit">登录</button>
      <button type="button" onClick={goBack}>返回</button>
    </form>
  );
}

useLocation — 获取当前位置信息

import { useLocation } from 'react-router-dom';

function Breadcrumb() {
  const location = useLocation();
  // location.pathname → "/dashboard/users/123"
  // location.search → "?tab=posts"
  // location.state → 通过 navigate 传递的状态对象
  return <nav>当前路径:{location.pathname}</nav>;
}

useSearchParams — 操作查询参数

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

function ProductList() {
  const [searchParams, setSearchParams] = useSearchParams();
  const page = Number(searchParams.get('page')) || 1;

  return (
    <div>
      <p>第 {page} 页</p>
      <button onClick={() => setSearchParams({ page: String(page + 1) })}>
        下一页
      </button>
    </div>
  );
}

五、v6.4+ Data API(数据路由)

v6.4 引入了 createBrowserRouterRouterProvider,支持 loader/action 模式,将数据获取与路由深度集成:

import {
  createBrowserRouter,
  RouterProvider,
  useLoaderData,
} from 'react-router-dom';

const router = createBrowserRouter([
  {
    path: '/',
    element: <RootLayout />,
    errorElement: <ErrorBoundary />,
    children: [
      {
        index: true,
        element: <Home />,
        loader: () => fetchDashboardStats(),
      },
      {
        path: 'users/:userId',
        element: <UserDetail />,
        loader: async ({ params }) => {
          const user = await fetchUser(params.userId!);
          if (!user) throw new Response('Not Found', { status: 404 });
          return user;
        },
        action: async ({ request, params }) => {
          const formData = await request.formData();
          await updateUser(params.userId!, Object.fromEntries(formData));
          return { success: true };
        },
      },
    ],
  },
]);

function App() {
  return <RouterProvider router={router} />;
}

function UserDetail() {
  const user = useLoaderData() as User;

  return (
    <div>
      <h1>{user.name}</h1>
      <Form method="post">
        <input name="email" defaultValue={user.email} />
        <button type="submit">更新</button>
      </Form>
    </div>
  );
}

loader — 路由数据加载

loader 在路由渲染之前执行,负责获取该路由所需的数据。它接收 paramsrequest 对象,可以抛出 Response 来触发 errorElement。

action — 处理表单提交

action 处理非 GET 请求(POST/PUT/DELETE),配合 <Form> 组件实现声明式数据变更。提交后 React Router 会自动重新调用相关路由的 loader 以更新数据。

六、路由守卫模式

React Router 没有内置路由守卫,但可以通过组件组合实现:

function RequireAuth({ children }: { children: React.ReactNode }) {
  const { user, isLoading } = useAuth();
  const location = useLocation();

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

// 在路由配置中使用
<Route
  path="/admin"
  element={
    <RequireAuth>
      <AdminLayout />
    </RequireAuth>
  }
>
  <Route index element={<AdminDashboard />} />
</Route>

七、总结

组件/Hook用途
BrowserRouter顶层路由容器,基于 History API
Routes / Route声明路由规则与组件映射
Link / NavLink客户端导航(NavLink 支持激活状态)
Navigate声明式重定向
Outlet嵌套路由的子组件渲染出口
useParams获取动态路由参数
useNavigate编程式导航
useLocation获取当前 URL 信息
useSearchParams读写查询参数
useLoaderData获取 loader 返回的数据

React Router v6 的核心变化:用 Routes 替代 Switch,用 element 替代 component/render,原生支持嵌套路由与相对路径,v6.4+ Data API 将路由与数据获取统一管理。