什么是React路由
React Router是React应用中最流行的路由库,它允许我们创建单页面应用(SPA),在不刷新页面的情况下实现不同页面之间的导航。
安装React Router
使用npm或yarn安装React Router:
1
2
3
| npm install react-router-dom
# 或
yarn add react-router-dom
|
官方安装文档
基础配置
在应用的根组件中配置路由:
1
2
3
4
5
6
7
8
9
10
11
12
| import { BrowserRouter as Router } from 'react-router-dom';
import App from './App';
function Root() {
return (
<Router>
<App />
</Router>
);
}
export default Root;
|
核心路由概念
路由器类型
React Router提供了几种不同的路由器:
- BrowserRouter: 使用HTML5 History API,URL看起来更自然(如
/about
) - HashRouter: 使用URL的hash部分(如
/#/about
),兼容性更好 - MemoryRouter: 在内存中保存历史记录,常用于测试
核心组件
- Routes: 路由容器,用于包裹所有Route组件
- Route: 定义单个路由规则
- Link: 声明式导航组件
- NavLink: 带有激活状态的导航组件
- Navigate: 编程式重定向组件
- Outlet: 用于渲染子路由组件
基本路由使用
创建简单路由
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
| import { Routes, Route, Link } from 'react-router-dom';
function App() {
return (
<div>
<nav>
<Link to="/">首页</Link>
<Link to="/about">关于我们</Link>
<Link to="/contact">联系我们</Link>
</nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/contact" element={<Contact />} />
</Routes>
</div>
);
}
function Home() {
return <h1>首页</h1>;
}
function About() {
return <h1>关于我们</h1>;
}
function Contact() {
return <h1>联系我们</h1>;
}
|
使用NavLink实现激活状态
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
| import { NavLink } from 'react-router-dom';
function Navigation() {
return (
<nav>
<NavLink
to="/"
className={({ isActive }) => isActive ? "active" : ""}
>
首页
</NavLink>
<NavLink
to="/about"
style={({ isActive }) => ({
color: isActive ? "#ff6b6b" : "#333"
})}
>
关于我们
</NavLink>
</nav>
);
}
## 动态路由和路由参数
### URL参数
```jsx
import { useParams } from 'react-router-dom';
function App() {
return (
<Routes>
<Route path="/user/:id" element={<UserProfile />} />
<Route path="/product/:category/:id" element={<Product />} />
</Routes>
);
}
function UserProfile() {
const { id } = useParams();
return <h1>用户ID: {id}</h1>;
}
function Product() {
const { category, id } = useParams();
return (
<div>
<h1>分类: {category}</h1>
<h2>产品ID: {id}</h2>
</div>
);
}
|
查询参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| import { useSearchParams } from 'react-router-dom';
function SearchResults() {
const [searchParams, setSearchParams] = useSearchParams();
const query = searchParams.get('q');
const page = searchParams.get('page') || '1';
const updateSearch = (newQuery) => {
setSearchParams({ q: newQuery, page: '1' });
};
return (
<div>
<h1>搜索结果: {query}</h1>
<p>当前页: {page}</p>
<button onClick={() => updateSearch('新搜索')}>
更新搜索
</button>
</div>
);
}
|
可选参数和通配符
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
| function App() {
return (
<Routes>
{/* 可选参数 */}
<Route path="/blog/:year?/:month?" element={<Blog />} />
{/* 通配符路由 */}
<Route path="/docs/*" element={<Documentation />} />
{/* 404页面 */}
<Route path="*" element={<NotFound />} />
</Routes>
);
}
## 嵌套路由
### 基本嵌套路由
```jsx
import { Outlet } from 'react-router-dom';
function App() {
return (
<Routes>
<Route path="/" element={<Layout />}>
<Route index element={<Home />} />
<Route path="about" element={<About />} />
<Route path="dashboard" element={<Dashboard />}>
<Route index element={<DashboardHome />} />
<Route path="profile" element={<Profile />} />
<Route path="settings" element={<Settings />} />
</Route>
</Route>
</Routes>
);
}
function Layout() {
return (
<div>
<nav>
<Link to="/">首页</Link>
<Link to="/about">关于</Link>
<Link to="/dashboard">控制台</Link>
</nav>
<main>
<Outlet />
</main>
</div>
);
}
function Dashboard() {
return (
<div>
<h1>控制台</h1>
<nav>
<Link to="/dashboard">概览</Link>
<Link to="/dashboard/profile">个人资料</Link>
<Link to="/dashboard/settings">设置</Link>
</nav>
<div>
<Outlet />
</div>
</div>
);
}
|
相对路径导航
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
| import { Link, useNavigate } from 'react-router-dom';
function Dashboard() {
const navigate = useNavigate();
return (
<div>
<h1>控制台</h1>
<nav>
{/* 相对路径 */}
<Link to=".">概览</Link>
<Link to="profile">个人资料</Link>
<Link to="settings">设置</Link>
{/* 绝对路径 */}
<Link to="/dashboard/analytics">数据分析</Link>
</nav>
<button onClick={() => navigate('profile')}>
前往个人资料
</button>
<Outlet />
</div>
);
}
## 路由守卫和编程式导航
### 编程式导航
```jsx
import { useNavigate, useLocation } from 'react-router-dom';
function LoginForm() {
const navigate = useNavigate();
const location = useLocation();
const from = location.state?.from?.pathname || '/dashboard';
const handleLogin = async (credentials) => {
try {
await login(credentials);
// 登录成功后跳转
navigate(from, { replace: true });
} catch (error) {
console.error('登录失败');
}
};
return (
<form onSubmit={handleLogin}>
{/* 表单内容 */}
<button type="submit">登录</button>
<button onClick={() => navigate(-1)}>
返回上一页
</button>
</form>
);
}
|
路由守卫
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
| import { Navigate, useLocation } from 'react-router-dom';
function RequireAuth({ children }) {
const isAuthenticated = useAuth();
const location = useLocation();
if (!isAuthenticated) {
return (
<Navigate
to="/login"
state={{ from: location }}
replace
/>
);
}
return children;
}
// 使用路由守卫
function App() {
return (
<Routes>
<Route path="/login" element={<Login />} />
<Route path="/dashboard" element={
<RequireAuth>
<Dashboard />
</RequireAuth>
} />
</Routes>
);
}
|
条件重定向
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
| function App() {
return (
<Routes>
<Route path="/" element={<Home />} />
<Route path="/old-path" element={<Navigate to="/new-path" replace />} />
<Route path="/admin" element={
<RequireRole role="admin">
<AdminPanel />
</RequireRole>
} />
</Routes>
);
}
function RequireRole({ role, children }) {
const user = useUser();
if (!user) {
return <Navigate to="/login" />;
}
if (!user.roles.includes(role)) {
return <Navigate to="/unauthorized" />;
}
return children;
}
## 最佳实践和总结
### 性能优化
#### 路由懒加载
```jsx
import { lazy, Suspense } from 'react';
const Dashboard = lazy(() => import('./Dashboard'));
const Profile = lazy(() => import('./Profile'));
function App() {
return (
<Routes>
<Route path="/dashboard" element={
<Suspense fallback={<div>加载中...</div>}>
<Dashboard />
</Suspense>
} />
<Route path="/profile" element={
<Suspense fallback={<div>加载中...</div>}>
<Profile />
</Suspense>
} />
</Routes>
);
}
|
预加载关键路由
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| import { Link } from 'react-router-dom';
function Navigation() {
const preloadDashboard = () => {
import('./Dashboard');
};
return (
<nav>
<Link
to="/dashboard"
onMouseEnter={preloadDashboard}
>
控制台
</Link>
</nav>
);
}
|
常见模式
面包屑导航
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| import { useLocation, Link } from 'react-router-dom';
function Breadcrumbs() {
const location = useLocation();
const pathnames = location.pathname.split('/').filter(x => x);
return (
<nav>
<Link to="/">首页</Link>
{pathnames.map((name, index) => {
const routeTo = `/${pathnames.slice(0, index + 1).join('/')}`;
const isLast = index === pathnames.length - 1;
return isLast ? (
<span key={name}> / {name}</span>
) : (
<span key={name}>
{' / '}
<Link to={routeTo}>{name}</Link>
</span>
);
})}
</nav>
);
}
|
路由状态管理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| import { useLocation } from 'react-router-dom';
function usePageTracking() {
const location = useLocation();
useEffect(() => {
// 页面访问统计
analytics.track('page_view', {
path: location.pathname,
search: location.search
});
}, [location]);
}
function App() {
usePageTracking();
return (
<Routes>
{/* 路由配置 */}
</Routes>
);
}
|
最佳实践总结
路由组织
- 使用扁平化的路由结构
- 合理使用嵌套路由
- 保持URL结构清晰易懂
性能优化
- 使用代码分割和懒加载
- 预加载关键路由
- 避免不必要的重新渲染
用户体验
- 提供加载状态
- 实现友好的404页面
- 使用路由守卫保护私有页面
SEO优化
- 使用BrowserRouter而不是HashRouter
- 设置适当的页面标题
- 考虑服务端渲染(SSR)
错误处理
- 实现全局错误边界
- 处理路由异常情况
- 提供用户友好的错误信息
React Router为React应用提供了强大而灵活的路由解决方案。通过掌握这些概念和最佳实践,你可以构建出用户体验良好、性能优秀的单页面应用。