📖 核心概念
- 客户端路由原理
- React Router v6路由配置
- 嵌套路由与布局路由
- 路由守卫与权限控制
- 动态路由与参数
- 懒加载与代码分割
💻 代码实现
React Router配置
✅ tsx
import {
createBrowserRouter, RouterProvider,
Outlet, Link, NavLink, useNavigate,
useParams, useSearchParams, useLocation,
Navigate, useLoaderData,
} from 'react-router-dom';
import { lazy, Suspense } from 'react';
// 懒加载路由组件
const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));
const Dashboard = lazy(() => import('./pages/Dashboard'));
// 布局组件
function RootLayout() {
return (
<div className="app">
<nav className="sidebar">
<NavLink to="/" end className={({ isActive }) => isActive ? 'active' : ''}>首页</NavLink>
<NavLink to="/about">关于</NavLink>
<NavLink to="/dashboard">控制台</NavLink>
</nav>
<main className="content">
<Suspense fallback={<div>加载中...</div>}>
<Outlet />
</Suspense>
</main>
</div>
);
}
// 路由守卫
function AuthGuard({ children }: { children: ReactNode }) {
const token = useStore(s => s.token);
const location = useLocation();
if (!token) {
return <Navigate to="/login" state={{ from: location }} replace />;
}
return <>{children}</>;
}
// 路由配置
const router = createBrowserRouter([
{
path: '/',
element: <RootLayout />,
children: [
{ index: true, element: <Home /> },
{ path: 'about', element: <About /> },
{
path: 'dashboard',
element: <AuthGuard><Dashboard /></AuthGuard>,
children: [
{ index: true, element: <DashboardHome /> },
{ path: 'settings', element: <Settings /> },
{ path: 'users/:userId', element: <UserProfile />,
loader: async ({ params }) => {
const user = await fetchUser(params.userId!);
return user;
},
},
],
},
{ path: 'login', element: <Login /> },
{ path: '*', element: <NotFound /> },
],
},
]);
function App() {
return <RouterProvider router={router} />;
}
高级路由模式
✅ tsx
// 路由参数与搜索参数
function ProductPage() {
const { productId } = useParams(); // 动态参数
const [searchParams, setSearchParams] = useSearchParams();
const page = Number(searchParams.get('page') || 1);
const sort = searchParams.get('sort') || 'latest';
const { data } = useFetch(\`/api/products/\${productId}?page=\${page}&sort=\${sort}\`);
return (
<div>
<h1>产品 {productId}</h1>
{/* 修改搜索参数不会触发页面跳转 */}
<select value={sort} onChange={e => {
setSearchParams(prev => { prev.set('sort', e.target.value); return prev; });
}}>
<option value="latest">最新</option>
<option value="price">价格</option>
</select>
</div>
);
}
// 编程式导航
function LoginForm() {
const navigate = useNavigate();
const location = useLocation();
const from = (location.state as any)?.from?.pathname || '/';
const handleSubmit = async (e: FormEvent) => {
e.preventDefault();
try {
await login(credentials);
navigate(from, { replace: true }); // 返回之前的页面
} catch (err) {
setError('登录失败');
}
};
}
// 面包屑导航
function Breadcrumbs() {
const location = useLocation();
const pathSegments = location.pathname.split('/').filter(Boolean);
const crumbs = pathSegments.map((segment, i) => {
const path = '/' + pathSegments.slice(0, i + 1).join('/');
const label = segment.charAt(0).toUpperCase() + segment.slice(1);
return { path, label };
});
return (
<nav className="breadcrumbs">
<Link to="/">首页</Link>
{crumbs.map(crumb => (
<span key={crumb.path}>
<span>/</span>
<Link to={crumb.path}>{crumb.label}</Link>
</span>
))}
</nav>
);
}
// 路由级别的数据加载
// 使用loader预加载数据,避免页面闪烁
const routes = [
{
path: '/blog',
element: <BlogLayout />,
loader: async () => {
const posts = await fetchPosts();
return { posts };
},
children: [
{
index: true,
element: <PostList />,
loader: async ({ request }) => {
const url = new URL(request.url);
const page = url.searchParams.get('page') || '1';
return { page: Number(page) };
},
},
],
},
];
🎯 练习任务
- 1 实现一个带权限控制的多级嵌套路由系统
- 2 创建面包屑导航组件,自动根据路由生成
- 3 实现路由懒加载,添加加载状态与错误边界
- 4 构建一个支持标签页保持状态的导航系统
🏆 成就解锁
路由导航员 — 掌握React Router,构建流畅的单页应用导航体验