说说你对 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 中的 Switch,Route 用于声明路径与组件的映射关系:
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 中 Route 的 element 属性接收 JSX 元素而非组件引用,path="*" 用于匹配所有未定义的路由(404 页面)。
Link 与 NavLink — 导航组件
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>
);
}
NavLink 是 Link 的增强版本,提供 isActive 和 isPending 状态,适用于导航栏高亮场景。
Navigate — 重定向组件
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/settings 时 Outlet 会渲染 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 引入了 createBrowserRouter 和 RouterProvider,支持 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 在路由渲染之前执行,负责获取该路由所需的数据。它接收 params 和 request 对象,可以抛出 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>
七、总结
React Router v6 的核心变化:用 Routes 替代 Switch,用 element 替代 component/render,原生支持嵌套路由与相对路径,v6.4+ Data API 将路由与数据获取统一管理。