一、什么是 React 路由?
在传统的网页开发中,每次点击链接或提交表单,浏览器都会向服务器发送请求并重新加载整个页面。这种模式不仅用户体验差,还增加了服务器负担。而前端路由的出现彻底改变了这一局面:它允许我们在不刷新页面的情况下切换视图,实现单页应用(SPA)的流畅体验。
React 是一个用于构建用户界面的 JavaScript 库,但它本身并不提供路由功能。因此,开发者需要借助第三方库 React Router 来管理路由。React Router 是 React 生态中最主流的路由解决方案,它通过定义 URL 与组件的映射关系,实现页面的动态切换。
二、React Router 的核心概念
1. 路由配置
React Router 的核心是通过配置路由规则,将不同的 URL 映射到对应的组件。例如:
jsx
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
import Home from './pages/Home';
import About from './pages/About';
function App() {
return (
<Router>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</Router>
);
}
BrowserRouter
:浏览器路由模式(默认使用 HTML5 History API)。Route
:定义路径与组件的映射关系。Routes
:包裹所有路由规则,确保只有一个匹配的路由生效。
2. 动态路由
动态路由允许路径中包含可变参数。例如:
jsx
<Route path="/user/:id" element={<UserProfile />} />
在组件中可以通过 useParams
钩子获取参数:
jsx
import { useParams } from 'react-router-dom';
function UserProfile() {
const { id } = useParams(); // 获取路径中的 id 参数
return <div>用户ID: {id}</div>;
}
3. 嵌套路由
嵌套路由用于实现页面的层级结构。例如:
jsx
<Route path="/user" element={<UserLayout />}>
<Route path=":id" element={<UserProfile />} />
<Route path="settings" element={<UserSettings />} />
</Route>
在父组件中需要使用 <Outlet />
渲染子路由:
jsx
import { Outlet } from 'react-router-dom';
function UserLayout() {
return (
<div>
<h1>用户中心</h1>
<Outlet /> {/* 子路由会在这里渲染 */}
</div>
);
}
三、React 路由的三种模式
React Router 提供了三种路由模式:BrowserRouter
、HashRouter
和 MemoryRouter
。它们的核心区别在于如何处理 URL 和服务器配置的需求。
1. BrowserRouter(浏览器路由)
- 原理 :使用 HTML5 的
history.pushState
API 动态修改 URL,而无需刷新页面。 - URL 特点 :路径中无
#
符号,例如https://example.com/about
。 - 优点 :
- URL 更加简洁美观。
- 支持 SEO(搜索引擎优化),因为搜索引擎可以索引完整路径。
- 缺点 :
- 需要服务器配置 :如果用户直接访问
/about
,服务器必须返回index.html
,否则会返回 404 错误。
- 需要服务器配置 :如果用户直接访问
- 适用场景 :
- 现代浏览器支持的项目。
- 需要 SEO 优化的网站。
- 服务端渲染(SSR)或静态站点生成(SSG)的应用。
配置示例:
jsx
import { BrowserRouter } from 'react-router-dom';
function App() {
return (
<BrowserRouter>
{/* 路由配置 */}
</BrowserRouter>
);
}
2. HashRouter(哈希路由)
- 原理 :利用 URL 的哈希部分(即
#
后的内容)实现路由,通过监听hashchange
事件更新页面。 - URL 特点 :路径中包含
#
,例如https://example.com/#/about
。 - 优点 :
- 无需服务器配置:适合静态托管(如 GitHub Pages)。
- 兼容性更好,支持旧版浏览器(如 IE9)。
- 缺点 :
- URL 不够美观。
- 搜索引擎可能忽略
#
后的内容,影响 SEO。
- 适用场景 :
- 纯前端静态托管项目。
- 旧版浏览器兼容性要求较高的场景。
配置示例:
jsx
import { HashRouter } from 'react-router-dom';
function App() {
return (
<HashRouter>
{/* 路由配置 */}
</HashRouter>
);
}
3. MemoryRouter(内存路由)
- 原理:不依赖浏览器 URL,路由状态保存在内存中。
- URL 特点:地址栏不会发生变化。
- 优点 :
- 适用于非浏览器环境(如 React Native、Electron 应用)。
- 路由状态完全由内存控制,适合测试场景。
- 缺点 :
- 刷新页面会丢失路由状态。
- 不适合需要直接访问特定页面的场景。
- 适用场景 :
- 移动端应用或桌面应用(如 Electron)。
- 单元测试或无需地址栏交互的场景。
配置示例:
jsx
import { MemoryRouter } from 'react-router-dom';
function App() {
return (
<MemoryRouter>
{/* 路由配置 */}
</MemoryRouter>
);
}
四、如何选择路由模式?
模式 | URL 示例 | 服务器配置需求 | SEO 友好 | 适用场景 |
---|---|---|---|---|
BrowserRouter | https://example.com/about |
需要配置 | 是 | 现代浏览器、SEO 优化网站 |
HashRouter | https://example.com/#/about |
不需要 | 否 | 静态托管、旧浏览器兼容场景 |
MemoryRouter | 无变化 | 无 | 无 | 移动端应用、测试环境 |
五、路由跳转与传参
1. 通过 URL 参数传递
在路径中定义参数,例如 /user/:id
,并通过 useParams
获取参数:
jsx
<Link to="/user/123">查看用户</Link>
function UserProfile() {
const { id } = useParams(); // 获取 id=123
return <div>用户ID: {id}</div>;
}
2. 通过查询字符串传递
在 URL 中附加查询参数,例如 /search?name=Tom
,并通过 useLocation
解析:
jsx
<Link to="/search?name=Tom">搜索</Link>
function SearchPage() {
const { search } = useLocation();
const params = new URLSearchParams(search);
const name = params.get('name'); // 获取 name=Tom
return <div>搜索名称: {name}</div>;
}
3. 通过状态对象传递
在跳转时传递一个状态对象,刷新页面后参数不会丢失:
jsx
<Link
to={{
pathname: '/user',
state: { name: 'Tom' }
}}
>查看用户</Link>
function UserProfile() {
const { state } = useLocation();
return <div>用户名称: {state?.name}</div>;
}
六、路由跳转的常见方法
1. 编程式导航
通过 useNavigate
钩子实现跳转:
jsx
import { useNavigate } from 'react-router-dom';
function Home() {
const navigate = useNavigate();
return (
<button onClick={() => navigate('/about')}>
跳转到 About 页面
</button>
);
}
2. 声明式导航
使用 <Link>
组件:
jsx
import { Link } from 'react-router-dom';
<Link to="/about">去 About 页面</Link>
七、常见问题与解决方案
1. 刷新页面报 404 错误
- 原因 :使用
BrowserRouter
时,服务器未正确配置重定向规则。 - 解决方案 :在服务器配置中,将所有请求重定向到
index.html
。例如:-
Nginx :
nginxlocation / { try_files $uri /index.html; }
-
Apache :
apache<IfModule mod_rewrite.c> RewriteEngine On RewriteBase / RewriteRule ^index\.html$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . /index.html [L] </IfModule>
-
2. 动态路由参数丢失
- 原因:刷新页面时,动态参数未正确传递。
- 解决方案 :使用
state
传递参数,而不是 URL 参数。
八、React 路由面试题解析
1. React Router 的三种路由模式有什么区别?
- BrowserRouter :使用 HTML5 History API,URL 无
#
,需要服务器配置。 - HashRouter :使用 URL 哈希部分,URL 包含
#
,无需服务器配置。 - MemoryRouter:不依赖 URL,路由状态保存在内存中,适合非浏览器环境。
2. 如何在 React 路由中传递参数?
- URL 参数 :通过路径定义参数(如
/user/:id
),使用useParams
获取。 - 查询字符串 :通过
?key=value
传递,使用useLocation
解析。 - 状态对象 :通过
state
传递,刷新页面后参数不会丢失。
3. 为什么使用 BrowserRouter 时刷新页面会报 404?
- 原因 :服务器未配置将所有路径重定向到
index.html
。 - 解决方案 :在服务器配置中添加重定向规则,确保所有请求返回
index.html
。
4. React Router 的嵌套路由如何实现?
-
在父路由中使用
<Outlet />
渲染子路由:jsxfunction ParentLayout() { return ( <div> <h1>父组件</h1> <Outlet /> {/* 子路由会在这里渲染 */} </div> ); } <Route path="/parent" element={<ParentLayout />}> <Route path="child" element={<ChildComponent />} /> </Route>
5. React Router 的动态路由如何实现?
-
在路径中使用
:参数名
定义动态参数,并通过useParams
获取:jsx<Route path="/user/:id" element={<UserProfile />} /> function UserProfile() { const { id } = useParams(); // 获取动态参数 id return <div>用户ID: {id}</div>; }
九、总结
React Router 是构建现代单页应用的核心工具,通过灵活的路由模式和丰富的功能,开发者可以轻松实现复杂的导航逻辑。无论是选择 BrowserRouter
的 SEO 友好性,还是 HashRouter
的兼容性,亦或是 MemoryRouter
的灵活性,都需要根据项目需求进行权衡。掌握路由的配置、动态路由、嵌套路由以及参数传递技巧,是成为优秀前端开发者的关键一步。