React Router DOM 完全指南
引言
在构建单页应用程序 (SPA) 时,客户端路由是不可或缺的一部分。它允许用户在不重新加载整个页面的情况下,在应用程序的不同视图之间进行导航,从而提供流畅的用户体验。React Router DOM 是 React 社区中最流行且功能强大的路由库,它使得在 React 应用中实现声明式路由变得轻而易举。
本指南将带你深入了解 React Router DOM 的核心概念、基本用法和高级特性,帮助你构建出功能完善且易于维护的 React 应用程序。
核心概念
在使用 React Router DOM 之前,了解其几个核心组件和 Hooks 至关重要。
BrowserRouter: 这是所有路由组件的顶层容器。它使用 HTML5 历史 API (pushState, replaceState 和 popstate 事件) 来保持 UI 与 URL 的同步。通常,你会将它包裹在你的根组件外面。Routes: 这是一个新的组件 (v6 版本引入),它取代了Switch。它会遍历其所有的Route子元素,并只渲染第一个与当前 URL 匹配的Route。Route: 用于将 URL 路径与组件进行匹配。它接收path(要匹配的 URL 路径) 和element(当路径匹配时渲染的 React 元素) 属性。Link: 用于在应用程序内部进行导航。它渲染成一个<a>标签,但会阻止浏览器重新加载页面,而是通过React Router DOM进行客户端导航。NavLink: 类似于Link,但它会根据当前 URL 是否匹配其to属性,自动添加active类或应用自定义样式,非常适合导航菜单。useNavigateHook: (v6 版本引入) 允许你在组件中以编程方式进行导航,例如在表单提交后重定向用户。useParamsHook: 用于访问当前路由的动态参数 (例如/users/:id中的id)。useLocationHook: 返回一个location对象,其中包含当前 URL 的信息,如路径名、查询参数等。useSearchParamsHook: (v6 版本引入) 用于读取和修改 URL 的查询字符串参数。Outlet: (v6 版本引入) 在嵌套路由中,Outlet组件会渲染其父路由中匹配的子路由元素。
基本用法
1. 安装 React Router DOM
首先,你需要在你的 React 项目中安装 react-router-dom:
“`bash
npm install react-router-dom
或者
yarn add react-router-dom
“`
2. 设置 BrowserRouter
在你的 src/index.js (或你的应用入口文件) 中,用 BrowserRouter 包裹你的根组件 App:
“`javascript
// src/index.js
import React from ‘react’;
import ReactDOM from ‘react-dom/client’;
import ‘./index.css’;
import App from ‘./App’;
import { BrowserRouter } from ‘react-router-dom’;
const root = ReactDOM.createRoot(document.getElementById(‘root’));
root.render(
);
“`
3. 定义路由 (Routes 和 Route)
在你的 src/App.js 或你希望定义路由的地方,使用 Routes 和 Route 来声明你的应用程序路径和对应的组件:
“`javascript
// src/App.js
import React from ‘react’;
import { Routes, Route } from ‘react-router-dom’;
import Home from ‘./components/Home’;
import About from ‘./components/About’;
import Contact from ‘./components/Contact’;
function App() {
return (
);
}
export default App;
“`
4. 导航 (Link 和 NavLink)
为了在页面之间导航,你需要使用 Link 或 NavLink 组件:
“`javascript
// src/App.js (导航示例)
import React from ‘react’;
import { Routes, Route, Link, NavLink } from ‘react-router-dom’;
import Home from ‘./components/Home’;
import About from ‘./components/About’;
import Contact from ‘./components/Contact’;
function App() {
return (
<hr />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/contact" element={<Contact />} />
</Routes>
</div>
);
}
export default App;
// src/index.css (NavLink 的 active-link 样式)
/ nav li a.active-link {
background-color: #007bff;
color: white;
} /
“`
高级特性
1. 路由参数 (useParams)
当你的路由需要根据 URL 中的动态部分来显示内容时 (例如用户详情页 /user/123),你可以使用路由参数。
“`javascript
// src/components/User.js
import React from ‘react’;
import { useParams } from ‘react-router-dom’;
function User() {
const { id } = useParams(); // 获取 URL 中的 :id 参数
return (
用户详情页
用户 ID: {id}
);
}
export default User;
// src/App.js 中的路由定义
//
“`
2. 嵌套路由 (Outlet)
嵌套路由允许你构建具有分层结构的 UI。父路由组件会渲染其自身的 UI,并在 Outlet 组件的位置渲染匹配到的子路由组件。
“`javascript
// src/components/Users.js (父组件,包含嵌套路由)
import React from ‘react’;
import { Routes, Route, Link, Outlet } from ‘react-router-dom’;
function UserList() {
return (
用户列表
- 用户 1
- 用户 2
);
}
function UserDetailNested() {
// 这里可以再次使用 useParams 获取嵌套路由中的 ID
return (
嵌套用户详情
这是嵌套路由中的用户详情页面。
);
}
function Users() {
return (
用户页面
{/* Outlet 会渲染匹配到的子路由 */}
<Outlet />
{/* 可以在这里定义嵌套的 Routes,但更推荐在父 Routes 中定义 */}
{/*
<Routes>
<Route path="/" element={<UserList />} />
<Route path=":id" element={<UserDetailNested />} />
<Route path="new" element={<h3>创建新用户表单</h3>} />
</Routes>
*/}
</div>
);
}
export default Users;
// src/App.js 中的路由定义 (注意 “/” 用于匹配所有子路径) } /> {/
//
//
//
//
//
// 注意:在 React Router v6 中,嵌套的 } />
// 示例:
//
//
//
//
//
“`
3. 编程导航 (useNavigate)
useNavigate Hook 返回一个函数,可以用于在组件内部触发导航,无需用户点击 Link。
“`javascript
// src/components/Login.js (示例:登录后跳转)
import React from ‘react’;
import { useNavigate } from ‘react-router-dom’;
function Login({ onLogin }) {
const navigate = useNavigate();
const handleLoginClick = () => {
onLogin(); // 模拟登录成功
navigate(‘/dashboard’); // 登录后跳转到仪表盘
};
return (
登录页面
);
}
export default Login;
“`
4. 重定向 (Navigate)
Navigate 组件允许你在组件渲染时立即触发重定向。
“`javascript
// src/App.js (示例:旧路径重定向到新路径)
import { Navigate } from ‘react-router-dom’;
// …
{/ … 其他路由 … /}
// …
``replace` 属性会替换历史堆栈中的当前条目,而不是添加新条目。
5. 404 Not Found 路由
使用 path="*" 可以匹配任何未被前面路由匹配到的路径,通常用于显示 404 页面。
“`javascript
// src/components/NotFound.js
import React from ‘react’;
function NotFound() {
return (
404 – 页面未找到
抱歉,您访问的页面不存在。
);
}
export default NotFound;
// src/App.js 中的路由定义 (放在 Routes 的最后)
//
“`
6. 保护路由
保护路由 (或私有路由) 确保只有经过身份验证的用户才能访问特定页面。这通常通过创建一个包装组件来实现。
“`javascript
// src/components/Protected.js
import React from ‘react’;
import { Navigate } from ‘react-router-dom’;
function Protected({ isAuthenticated, children }) {
if (!isAuthenticated) {
// 如果未认证,重定向到登录页
return
}
return children; // 如果认证通过,渲染子组件
}
export default Protected;
// src/App.js 中的使用
//
// \
// \
// }
// />
“`
7. 查询参数 (useSearchParams)
查询参数是 URL 中 ? 后面跟着的键值对 (例如 /search?query=react&page=1)。useSearchParams Hook 允许你轻松地读取和修改它们。
“`javascript
// src/components/QueryParams.js
import React from ‘react’;
import { useSearchParams } from ‘react-router-dom’;
function QueryParams() {
const [searchParams, setSearchParams] = useSearchParams();
const name = searchParams.get(‘name’);
const age = searchParams.get(‘age’);
const handleUpdateParams = () => {
// 设置新的查询参数,这会更新 URL
setSearchParams({ city: ‘NewYork’, country: ‘USA’ });
};
return (
查询参数示例
URL 中的名字: {name || ‘N/A’}
URL 中的年龄: {age || ‘N/A’}
当前 URL 查询字符串: {searchParams.toString()}
);
}
export default QueryParams;
// src/App.js 中的路由定义
//
“`
结论
React Router DOM 是 React 应用程序中管理路由的强大工具。通过理解 BrowserRouter、Routes、Route、Link 和 NavLink 等核心组件,以及 useNavigate、useParams 和 useSearchParams 等 Hooks,你可以构建出具有良好用户体验和复杂导航逻辑的单页应用程序。
熟练掌握这些概念和实践,将使你能够更好地组织代码、提高应用性能,并为用户提供无缝的导航体验。希望这篇完全指南能帮助你更好地利用 React Router DOM 构建出色的 React 应用。
注意: 上述代码示例是基于 React Router DOM v6 版本。如果你使用的是旧版本,某些 API 可能有所不同。