在react中使用react-router
1. 为什么我们需要 React Router?
传统的网页应用,每次点击链接都会向服务器发送请求,然后服务器返回一个全新的 HTML 页面,导致页面白屏和加载延迟。而单页应用(SPA)的核心理念是:只加载一次 HTML 页面,然后通过 JavaScript 动态地更新页面内容。
问题: 既然只有一个 HTML 页面,那用户怎么能在“不同页面”之间切换呢?比如从“首页”到“关于我们”页面,URL 怎么变化?
解决方案:
这就是
客户端路由
的作用。React Router 就是最流行的客户端路由库之一,它帮助我们:
- 将 URL 映射到 React 组件: 当 URL 变化时,它会渲染对应的组件。
- 实现无刷新导航: 页面内容在客户端动态更新,避免了白屏。
- 管理历史记录: 支持浏览器的前进、后退按钮。
2. React Router 的核心概念和组件
React Router v6+ 引入了一些新的概念和更简洁的 API。
BrowserRouter
:- 作用: 它是整个路由功能的“容器”或者说“路由器”。你需要在你的 React 应用的最顶层(通常是
App.js
的return
语句中,或者index.js
的ReactDOM.render
部分)用它包裹住所有需要路由功能的组件。 - 原理: 它使用 HTML5 History API (PushState, ReplaceState) 来保持 UI 与 URL 的同步,是 Web 应用最常用的路由器。
JavaScript
// src/index.js (或者 App.js) import React from 'react'; import ReactDOM from 'react-dom/client'; import { BrowserRouter } from 'react-router-dom'; // 导入 BrowserRouter import App from './App'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <React.StrictMode> <BrowserRouter> {/* 用 BrowserRouter 包裹你的应用 */} <App /> </BrowserRouter> </React.StrictMode> );
- 作用: 它是整个路由功能的“容器”或者说“路由器”。你需要在你的 React 应用的最顶层(通常是
Routes
:- 作用: 它是一个容器,里面包含了一组
Route
组件。Routes
的智能之处在于,它会遍历其内部的Route
组件,然后只渲染第一个匹配当前 URL 的Route
。 - 特点: 解决了 v5 中
Switch
的痛点,更简洁。
JavaScript
// src/App.js import { Routes, Route } from 'react-router-dom'; import HomePage from './pages/HomePage'; import AboutPage from './pages/AboutPage'; import ContactPage from './pages/ContactPage'; import NotFoundPage from './pages/NotFoundPage'; function App() { return ( <Routes> {/* Routes 容器 */} <Route path="/" element={<HomePage />} /> {/* 匹配 "/" 渲染 HomePage */} <Route path="/about" element={<AboutPage />} /> {/* 匹配 "/about" 渲染 AboutPage */} <Route path="/contact" element={<ContactPage />} /> {/* 匹配 "/contact" 渲染 ContactPage */} <Route path="*" element={<NotFoundPage />} /> {/* 匹配所有未定义的路径,通常用于 404 页面 */} </Routes> ); }
- 作用: 它是一个容器,里面包含了一组
Route
:作用:
定义一个路由规则。它有两个主要属性:
path
:一个字符串,表示要匹配的 URL 路径。element
:当path
匹配时,要渲染的 React 元素(通常是你的组件)。
注意: v6 移除了
component
和render
prop,统一使用element
prop。
JavaScript
<Route path="/products/:productId" element={<ProductDetail />} /> {/* 带有动态参数 */}
Link
:- 作用: 这是在 React 应用中进行页面导航的首选方式。它会渲染成一个
<a>
标签,但点击时不会触发浏览器进行完整的页面刷新,而是由 React Router 接管,进行客户端路由跳转。 - 属性: 最常用的是
to
,指定要跳转的路径。
JavaScript
import { Link } from 'react-router-dom'; function Navigation() { return ( <nav> <Link to="/">首页</Link> <Link to="/about">关于我们</Link> <Link to="/contact">联系方式</Link> </nav> ); }
- 作用: 这是在 React 应用中进行页面导航的首选方式。它会渲染成一个
3. React Router v6+ 的核心 Hook
在函数组件中,我们通常使用 Hook 来获取路由相关的信息或进行编程导航。
useParams()
:获取 URL 中的动态参数作用: 当你的路由路径中包含动态部分(例如
/users/:id
),useParams
Hook 可以让你轻松地获取这些动态参数的值。例子:
JavaScript
// 定义路由:<Route path="/users/:userId" element={<UserProfile />} /> import { useParams } from 'react-router-dom'; function UserProfile() { const { userId } = useParams(); // URL 是 /users/123,userId 就会是 "123" return <h2>用户 ID: {userId}</h2>; }
useNavigate()
:进行编程式导航作用: 当你需要在事件处理器(如表单提交后)、
useEffect
中或任何不能直接使用Link
的地方进行页面跳转时,可以使用useNavigate
。它返回一个Maps
函数。例子:
JavaScript
import { useNavigate } from 'react-router-dom'; function LoginForm() { const navigate = useNavigate(); const handleSubmit = (e) => { e.preventDefault(); // ... 处理登录逻辑 ... const success = true; // 假设登录成功 if (success) { navigate('/dashboard'); // 导航到仪表盘页面 // navigate(-1); // 返回上一页 // navigate('/some-path', { replace: true }); // 替换当前历史记录而不是推入新记录 } }; return ( <form onSubmit={handleSubmit}> {/* ... 表单内容 ... */} <button type="submit">登录</button> </form> ); }
useLocation()
:获取当前 URL 的信息作用: 返回一个
location
对象,包含当前 URL 的各种信息,如pathname
(路径),search
(查询参数),hash
(哈希值),state
(通过Maps
传递的状态)。例子:
JavaScript
import { useLocation } from 'react-router-dom'; function CurrentPageInfo() { const location = useLocation(); console.log(location.pathname); // 当前路径,如 "/about" console.log(location.search); // 查询参数,如 "?name=Alice" console.log(location.state); // navigate 传过来的 state return ( <p>你当前在: {location.pathname}</p> ); }
useOutlet()
:渲染嵌套路由的子组件- 作用: 在父级路由组件中,
useOutlet
(或直接使用<Outlet />
组件)用于渲染其匹配的子路由组件。这是实现嵌套路由的关键。
- 作用: 在父级路由组件中,
4. 嵌套路由:构建复杂布局
嵌套路由允许你将父组件的布局和子组件的特定内容结合起来。
口述图示:嵌套路由的结构
想象你有一个用户管理页面 /users。这个页面有导航栏和侧边栏,而它的子路由 /users/profile 和 /users/settings 只改变右侧的内容区域。
// App.js (主路由文件) <Routes> <Route path="/" element={<HomePage />} /> <Route path="/users" element={<UserLayout />}> {/* 父级路由,渲染 UserLayout */} <Route index element={<UserDashboard />} /> {/* 默认子路由,当 path="/users" 时渲染 */} <Route path="profile" element={<UserProfile />} /> {/* 完整路径 /users/profile */} <Route path="settings" element={<UserSettings />} /> {/* 完整路径 /users/settings */} </Route> {/* ... 其他路由 ... */} </Routes> // UserLayout.js (父级路由组件) import { Outlet, Link } from 'react-router-dom'; function UserLayout() { return ( <div> <h1>用户管理</h1> <nav> <Link to="/users">仪表盘</Link> | {' '} <Link to="/users/profile">个人资料</Link> | {' '} <Link to="/users/settings">设置</Link> </nav> <hr /> <div style={{ border: '1px solid gray', padding: '10px' }}> {/* Outlet 会在这里渲染匹配到的子路由组件 */} <Outlet /> </div> </div> ); } // UserDashboard.js, UserProfile.js, UserSettings.js 都是普通的 React 组件
注意
index
路由:index
路由表示当父级路由路径完全匹配(没有子路径)时,默认渲染的子组件。子路由
path
: 子路由的path
不需要以/
开头,它会自动相对于父路由的path
。例如,path="profile"
意味着完整路径是/users/profile
。
5. 重要的考虑和最佳实践
404 Not Found 页面:
- 使用
Routes
容器的最后一个Route
,将path
设置为*
(星号,通配符)。它会匹配所有未被前面路由匹配的路径。 <Route path="*" element={<NotFoundPage />} />
- 使用
路由守卫(Authentication / Protected Routes):
React Router 本身不提供内置的路由守卫功能,但你可以通过在
element
Prop 中嵌套一个高阶组件 (HOC) 或一个条件渲染组件来实现。例如:
JavaScript
// ProtectedRoute.js import { Navigate } from 'react-router-dom'; import { useAuth } from './authContext'; // 假设你有一个认证 context function ProtectedRoute({ children }) { const { isAuthenticated } = useAuth(); // 检查用户是否认证 if (!isAuthenticated) { return <Navigate to="/login" replace />; // 未认证则重定向到登录页 } return children; // 认证通过则渲染子组件 } // App.js <Routes> <Route path="/login" element={<LoginPage />} /> <Route path="/dashboard" element={ <ProtectedRoute> <DashboardPage /> </ProtectedRoute> } /> </Routes>
使用
useResolvedPath
或useMatch
进行更高级的路径匹配和解析。性能优化:
结合
React.lazy
和
Suspense
对路由组件进行
懒加载
,可以显著提升应用的首次加载速度。
JavaScript
import React, { lazy, Suspense } from 'react'; import { Routes, Route } from 'react-router-dom'; const HomePage = lazy(() => import('./pages/HomePage')); const AboutPage = lazy(() => import('./pages/AboutPage')); function App() { return ( <Suspense fallback={<div>加载中...</div>}> <Routes> <Route path="/" element={<HomePage />} /> <Route path="/about" element={<AboutPage />} /> </Routes> </Suspense> ); }
总结与展望
React Router 是构建现代单页应用的基石。它让你的应用在没有页面刷新的情况下实现复杂的导航,大大提升了用户体验。
- 核心组件:
BrowserRouter
(路由器容器),Routes
(路由集合),Route
(单个路由规则),Link
(声明式导航)。 - 核心 Hook:
useParams
(获取路径参数),useNavigate
(编程导航),useLocation
(获取当前路由信息),useOutlet
(渲染嵌套子路由)。 - 最佳实践: 拥抱 v6 的简洁 API,合理使用嵌套路由,利用
*
路径处理 404,通过lazy
/Suspense
进行代码分割,并考虑如何实现路由守卫。
掌握 React Router,你就能搭建起复杂且用户体验极佳的单页应用。多动手实践,尝试不同的路由场景,你很快就能成为路由高手!
继续加油!你的前端技能树正在茁壮成长!