Skip to main content

React Router

关于 React Router 相关知识点总结, 持续更新中……

基础概念

React Router 是 React 应用程序的标准路由库,最新版本带来了许多现代化的路由特性。

主要组件

  • BrowserRouter: 使用 HTML5 history API 的路由器
  • Routes: 路由匹配容器
  • Route: 路由规则定义
  • Link: 导航链接
  • NavLink: 带有激活状态的导航链接
  • Navigate: 编程式导航组件
  • Outlet: 子路由渲染位置

基本用法

1. 路由配置

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

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

2. 导航链接

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

function Navigation() {
return (
<nav>
<Link to="/">首页</Link>
<NavLink
to="/about"
className={({ isActive }) => isActive ? 'active' : ''}
>
关于
</NavLink>
</nav>
);
}

3. 嵌套路由

function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Layout />}>
<Route index element={<Home />} />
<Route path="dashboard" element={<Dashboard />}>
<Route path="profile" element={<Profile />} />
<Route path="settings" element={<Settings />} />
</Route>
</Route>
</Routes>
</BrowserRouter>
);
}

function Layout() {
return (
<div>
<nav>{/* 导航栏 */}</nav>
<Outlet /> {/* 子路由渲染位置 */}
</div>
);
}

高级特性

1. 路由守卫

function PrivateRoute({ children }) {
const auth = useAuth(); // 自定义 hook 检查认证状态

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

return children;
}

// 使用方式
<Route
path="/dashboard"
element={
<PrivateRoute>
<Dashboard />
</PrivateRoute>
}
/>

2. 路由数据加载

import { useLoaderData, defer, Await } from 'react-router-dom';

// 定义数据加载器
export async function loader({ params }) {
const user = await fetchUser(params.id);
const posts = fetchUserPosts(params.id); // 不等待完成

return defer({
user,
posts
});
}

// 组件中使用
function UserProfile() {
const { user, posts } = useLoaderData();

return (
<div>
<h1>{user.name}</h1>
<Suspense fallback={<Loading />}>
<Await resolve={posts}>
{(posts) => (
<ul>
{posts.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)}
</Await>
</Suspense>
</div>
);
}

3. 路由操作(Actions)

// 定义操作
export async function action({ request }) {
const formData = await request.formData();
const updates = Object.fromEntries(formData);

await updateUser(updates);
return redirect('/dashboard');
}

// 组件中使用
function UpdateProfile() {
const { state } = useNavigation();

return (
<Form method="post">
<input name="name" type="text" />
<button type="submit" disabled={state === 'submitting'}>
{state === 'submitting' ? '更新中...' : '更新'}
</button>
</Form>
);
}

4. URL 参数和查询字符串

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

function ProductPage() {
const { id } = useParams();
const [searchParams, setSearchParams] = useSearchParams();
const sort = searchParams.get('sort');

return (
<div>
<h1>产品 {id}</h1>
<select
value={sort || 'name'}
onChange={e => setSearchParams({ sort: e.target.value })}
>
<option value="name">按名称排序</option>
<option value="price">按价格排序</option>
</select>
</div>
);
}

最佳实践

  1. 路由组织
    • 使用懒加载优化大型应用
    • 合理使用嵌套路由
    • 实现统一的错误边界
const Dashboard = lazy(() => import('./pages/Dashboard'));

<Routes>
<Route path="/dashboard" element={
<Suspense fallback={<Loading />}>
<Dashboard />
</Suspense>
} />
</Routes>
  1. 数据获取

    • 使用 loader 函数预加载数据
    • 合理使用 defer 延迟加载非关键数据
    • 实现优雅的加载状态
  2. 表单处理

    • 使用 action 函数处理表单提交
    • 实现乐观更新
    • proper 错误处理

性能优化

  1. 路由预加载
const preloadDashboard = () => {
const link = document.createElement('link');
link.rel = 'modulepreload';
link.href = '/dashboard.js';
document.head.appendChild(link);
};
  1. 数据预取
function NavLink({ to, children }) {
const prefetch = usePrefetch(to);

return (
<Link
to={to}
onMouseEnter={prefetch}
onFocus={prefetch}
>
{children}
</Link>
);
}

常见问题

  1. 404 处理
<Routes>
{/* 其他路由 */}
<Route path="*" element={
<div>
<h1>404 - 页面不存在</h1>
<Link to="/">返回首页</Link>
</div>
} />
</Routes>
  1. 路由切换动画
import { useLocation } from 'react-router-dom';
import { AnimatePresence, motion } from 'framer-motion';

function AnimatedRoutes() {
const location = useLocation();

return (
<AnimatePresence mode="wait">
<motion.div
key={location.pathname}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
>
<Routes location={location}>
{/* 路由配置 */}
</Routes>
</motion.div>
</AnimatePresence>
);
}

参考资源