01. React Router v6 的基本概念
React Router 是 React 事实上的路由标准。v6 相比 v5 有了重大变化——API 更简洁、路由匹配更智能、全面拥抱 React Hooks。
核心组件:
BrowserRouter——包裹整个应用,提供浏览器历史记录。
Routes——路由表容器,里面是一堆 Route。
Route——定义路径和组件的映射。path 是 URL 路径,element 是渲染的组件。
Link——导航链接,点击不刷新页面。相当于 a 标签但不会重新加载。
Outlet——子路由的插槽。父路由组件里放一个 Outlet,子路由组件就渲染在那里。
useParams——在组件里读取 URL 参数(如 /user/:id 中的 id)。
useNavigate——编程式导航,跳转到指定路径。
useLocation——获取当前 URL 信息。
jsx
import { BrowserRouter, Routes, Route, Link, useParams } from 'react-router-dom';
function App() {
return (
<BrowserRouter>
<nav>
<Link to="/">Home</Link>
<Link to="/users">Users</Link>
</nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/users" element={<UserList />} />
<Route path="/users/:id" element={<UserDetail />} />
<Route path="*" element={<NotFound />} />
</Routes>
</BrowserRouter>
);
}
function UserDetail() {
const { id } = useParams();
return <div>User: {id}</div>;
}02. 嵌套路由——Layout 布局模式
嵌套路由是 React Router v6 最强大的特性之一。你可以在父路由里定义共享的 Layout,子路由的内容通过 Outlet 渲染在 Layout 里的某个位置。
典型场景:后台管理系统——侧边栏和顶部导航是共用的,中间内容区根据路由变化。
嵌套路由的写法:父 Route 不设 path(或设为共享前缀),子 Route 直接写在父 Route 里面。子路由的 path 是相对路径,自动拼接。
Outlet 是子路由的占位符。Layout 组件里放一个 Outlet,子路由匹配到的组件就在这渲染。
useOutletContext——父路由可以通过 Outlet context prop 传数据给子路由。比 Context 更直接。
jsx
// 嵌套路由——后台管理系统布局
function AdminLayout() {
return (
<div className="admin">
<Sidebar />
<div className="content">
<Outlet />
</div>
</div>
);
}
function App() {
return (
<Routes>
<Route path="/" element={<Home />} />
<Route path="/admin" element={<AdminLayout />}>
<Route index element={<Dashboard />} />
<Route path="users" element={<Users />} />
<Route path="users/:id" element={<UserEdit />} />
<Route path="settings" element={<Settings />} />
</Route>
</Routes>
);
}
// /admin → Dashboard
// /admin/users → Users
// /admin/users/1 → UserEditindex 路由是父路径精确匹配时渲染的默认子路由。Route index 不加 path,只在父路径完全匹配时显示。
03. 路由守卫——权限控制
实际项目里不是所有页面都能公开访问。需要登录的页面要加路由守卫——没登录就重定向到登录页。
实现方式:写一个 ProtectedRoute 组件包裹需要保护的路由。在组件里检查是否登录(从 Context/Zustand/Redux 里拿用户状态),没登录就 Navigate 到登录页。
v6 的做法不是装饰器式的,而是用元素包裹:element={<ProtectedRoute><Dashboard /></ProtectedRoute>}。
更灵活的方案是用一个配置数组定义路由表,每个路由项加 requiresAuth 标记。统一遍历生成路由。
jsx
// 路由守卫组件
function ProtectedRoute({ children }) {
const user = useAuthStore(s => s.user);
if (!user) {
return <Navigate to="/login" replace />;
}
return children;
}
// 使用
<Routes>
<Route path="/login" element={<Login />} />
<Route path="/dashboard" element={
<ProtectedRoute>
<Dashboard />
</ProtectedRoute>
} />
</Routes>
// 或者用布局路由统一保护
<Route element={<ProtectedRoute />}>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/settings" element={<Settings />} />
</Route>04. 编程式导航与参数传递
useNavigate 返回一个 navigate 函数,可以编程跳转——表单提交后跳到成功页、按钮点击跳转。
跳转方式:
navigate('/path')——直接跳到目标路径。
navigate(-1)——后退一页。
navigate('/path', { replace: true })——替换当前历史记录而不是新增(用户按后退不会回到当前页)。
navigate('/path', { state: { data } })——传递状态给目标页面。
接收 state——目标页面用 useLocation() 获取 location.state。但注意:直接访问 URL(不是从导航跳转来的)时 state 是 null。
URL 参数(useParams)比 state 更可靠——参数存在 URL 里,刷新后还在。state 存在浏览器的 history 里,刷新可能会丢。
jsx
function SubmitForm() {
const navigate = useNavigate();
const handleSubmit = async (data) => {
await saveData(data);
navigate('/success', { state: { message: '保存成功' } });
};
return <form onSubmit={...}>...</form>;
}
function SuccessPage() {
const location = useLocation();
const message = location.state?.message || '操作成功';
return <div>{message}</div>;
}
// 后退
// navigate(-1);URL 参数是持久化的(刷新还在),state 是临时的。需要刷新后还能继续的信息放 URL 参数而不是 state。
05. React Router v6 的常见问题
1. BrowserRouter vs HashRouter——BrowserRouter 需要服务器配置(所有路径返回 index.html),HashRouter 不需要但 URL 里会有 #。生产环境推荐 BrowserRouter。
2. 路由不匹配——v6 的路由匹配默认精确。/users 不会匹配 /users/123,除非你在 /users 路由下写了一个相对路由。
3. 404 页面——用 Route path="*" 兜底,匹配所有不认识的路径。
4. 相对路径 & 绝对路径——Link to="about" 是相对于当前路径,Link to="/about" 是绝对路径。嵌套路由里容易搞混。
5. useSearchParams——获取和设置 URL 的查询参数(?key=value)。像 useState 一样用。
jsx
// HashRouter 不需要服务端配置
import { HashRouter } from 'react-router-dom';
// 404 兜底
<Route path="*" element={<NotFound />} />
// useSearchParams
function SearchPage() {
const [searchParams, setSearchParams] = useSearchParams();
const keyword = searchParams.get('q') || '';
return (
<input
value={keyword}
onChange={e => setSearchParams({ q: e.target.value })}
/>
);
}BrowserRouter 需要 Nginx 配 try_files $uri /index.html。不然直接访问 /users/123 会 404。HashRouter 不需要但 URL 不够干净。
知识测验
第 1/5 题正确 0
React Router v6 的 Outlet 组件是什么?
下一节
下一节 性能优化