React 与 React Router:前端路由的演进与实践
在现代前端开发中,React 已经成为构建用户界面的核心框架之一。它具有组件化、响应式状态管理、函数式编程风格等优势。然而,React 本身专注于视图层(View Layer),并不直接提供页面之间的导航机制。
这就引出了我们今天要重点探讨的内容:React Router —— React 生态系统中负责实现前端路由的重要工具。
一、什么是路由?为什么需要前端路由?
-
前端路由 vs 后端路由
- 后端路由:主要负责暴露 API 接口资源,处理 HTTP 请求并返回数据(如 JSON)或完整的 HTML 页面。
- 前端路由:负责在单页应用(SPA)中切换不同的页面级组件,不刷新整个页面,提升用户体验。
-
路由的历史演变
传统 MVC 架构
早期 Web 开发采用的是后端驱动的方式,用户访问 URL → 后端路由匹配 → 返回完整 HTML 页面(数据嵌套在页面中),每次页面切换都需要一次新的请求和响应。这种模式前后端耦合严重,不利于快速迭代和团队协作。
现代 MVVM 架构(前后端分离)
随着技术发展,前后端开始解耦:
- 后端专注于接口开发(API)
- 前端通过
fetch获取数据,渲染页面 - 前端也引入了路由机制,实现页面间切换
这就是我们常说的前端路由,而 React 中最常用的实现就是 react-router-dom。
二、React 全家桶中的 react-router-dom
**React 本身只负责组件、状态、Hooks 等核心功能。**为了4保持轻量和专注,官方将一些通用功能交由社区维护,比如:
| 包名 | 功能说明 |
|---|---|
react | 核心库:组件、状态、Hooks |
react-dom | DOM 渲染 |
react-router-dom | 前端路由支持 |
redux / mobx | 状态管理 |
axios | 发起网络请求 |
其中,react-router-dom 是用于 Web 浏览器环境的路由解决方案,目前广泛使用的是 v6 版本。
版本号格式如 7.6.3,其含义如下:
- 主版本号(7):重大变更
- 次版本号(6):新增功能但保持兼容
- 修订号(3):小 bug 修复或优化
三、React 的“特殊”之处:动态路由与 RESTful 设计
动态路由设计
React 支持非常灵活的 URL 定义方式,例如:
<Route path="/user/:id" element={<UserDetail />} />
这里的 :id 就是一个路径参数(params),可以通过 useParams() 钩子获取:
import { useParams } from 'react-router-dom';
function UserDetail() {
const { id } = useParams();
return <div>用户ID: {id}</div>;
}
这使得我们可以根据 URL 动态加载不同内容,非常适合像用户详情页、文章页等场景。
RESTful 风格与 URL 设计
RESTful 是一种国际通用的 API 设计规范,强调通过 URL 和 HTTP 方法来操作资源:
| 方法 | 示例 | 操作说明(以文章为例) |
|---|---|---|
| GET | /user/123 | 获取用户信息 |
| POST | /post | 创建新文章 |
| PUT | /post/456 | 替换整篇文章 |
| PATCH | /post/456 | 局部更新文章 |
| DELETE | /post/456 | 删除文章 |
React Router 非常适合配合 RESTful API 使用,前端通过路由定义页面结构,后端则提供数据接口。
四、React Router 的基本用法
-
安装
npm install react-router-dom -
App.jsx
import { useState } from "react"; import Home from "./pages/Home"; import About from "./pages/About"; import UserProfile from "./pages/UserProfile"; import Products from "./pages/Products"; import ProductDetails from "./pages/Products/ProductDetails"; import ProductNew from "./pages/Products/ProductNew"; import { BrowserRouter as Router, // 前端路由 Routes, // 路由设置容器 Route, // 单条路由 Link, } from "react-router-dom"; import "./App.css"; function App() { const [id, setId] = useState("123"); return ( <> {/* 前端路由接管路由 */} <Router> <nav> <Link to="/">首页</Link> <br /> <Link to="/about">关于</Link> <br /> <Link to="/user/123">用户</Link> <br /> <Link to="/products">产品</Link> </nav> {/* 路由设置容器 */} <Routes> <Route path="/" element={ <Home />} /> <Route path="/about" element={<About />} /> {/* 动态路由 */} <Route path="/user/:id" element={<UserProfile id={id} />} /> <Route path="/products" element={<Products />}> {/* 子路由 二级页面 */} <Route path=":productId" element={<ProductDetails />} /> {/* 子路由 二级页面 */} <Route path="new" element={<ProductNew />} /> </Route> </Routes> </Router> </> ); } export default App;子组件
在这些组件中,Home为首页,About、UserProfile、Products与Home同级,而ProductDetails、ProductNew是Products的子组件
// 首页 export default function Home(){ // 页面级别组件 return( <> <h1>Home</h1> </> ) } // About export default function About(){ return ( <div> <h1>About</h1> </div> ) } // 用户详情页 import { useParams } from "react-router-dom"; export default function UserProfile() { const {id} = useParams(); // 返回路由参数对象 return ( <> <h1>用户详情页</h1> <p>用户id: {id}</p> </> ); } // 产品 import { Outlet, Link } from "react-router-dom"; export default function Products(){ return ( <> <h1>Products</h1> <Link to="/products/new">New Product</Link><br /> <Link to="/products/1">Product Details</Link> <Outlet /> </> ) } export default function ProductDetails(){ return ( <> <h1>产品详情</h1> </> ) } export default function ProductNew(){ return ( <> <h1>新产品</h1> </> ) }效果展示
五、React Router 的高级特性
-
编程式导航(useNavigate)
import { useNavigate } from 'react-router-dom'; function LoginButton() { const navigate = useNavigate(); function handleLogin() { // 登录成功后跳转 navigate('/dashboard'); } return <button onClick={handleLogin}>登录</button>; } -
懒加载 + 代码分割
const LazyDashboard = React.lazy(() => import('./pages/Dashboard')); <Route path="/dashboard" element={ <React.Suspense fallback="加载中..."> <LazyDashboard /> </React.Suspense> } /> -
404 页面处理
<Route path="*" element={<NotFound />} />