114 lines
3.9 KiB
Markdown
114 lines
3.9 KiB
Markdown
---
|
||
tags: [frontend, react, react-router, routing, spa]
|
||
create time: 2026-04-25 15:30
|
||
---
|
||
|
||
# 路由原理:BrowserRouter 与组件协作
|
||
|
||
## 概述
|
||
|
||
本文梳理 React Router 的核心组件如何配合完成单页应用(SPA)的路由跳转。重点理解 `<BrowserRouter>` 作为"路由引擎"的作用,以及 `NavLink`、`Routes`、`Route`、`Navigate` 等组件的职责分工。
|
||
|
||
---
|
||
|
||
## 正文
|
||
|
||
### `<BrowserRouter>` 的核心作用
|
||
|
||
`<BrowserRouter>` 就是 React Router 的**"总开关"和"运行环境"**,没有它,路由组件无法正常工作。
|
||
|
||
```tsx
|
||
<BrowserRouter>
|
||
<App />
|
||
</BrowserRouter>
|
||
```
|
||
|
||
它做三件事:
|
||
|
||
1. **提供路由上下文** — 让 `Routes`、`Route`、`NavLink` 知道当前路径是什么、该显示哪个页面
|
||
2. **无刷新切换页面** — 基于浏览器的 `History API`,URL 变了但整页不会刷新
|
||
3. **全局可用** — 因为它包住了 `<App />`,其所有子组件都能使用路由能力
|
||
|
||
> 💡 **想一想**:为什么 `<BrowserRouter>` 必须包裹 `<App />` 而不是只包某个子组件?
|
||
>
|
||
> 如果它只包了 `<Routes>` 但没有包 `<NavLink>`,点击导航按钮会报错:
|
||
>
|
||
> ```
|
||
> Uncaught Error: useHref() may be used only in the context of a <Router> component
|
||
> ```
|
||
>
|
||
> 因为 `NavLink` 内部通过 React Context 读取路由数据,如果它处于 `<BrowserRouter>` 之外,就**读不到任何路由信息**,只能直接崩溃。
|
||
|
||
### 组件职责类比
|
||
|
||
| 组件 | 类比角色 | 一句话说明 |
|
||
|------|---------|-----------|
|
||
| `<BrowserRouter>` | 交通系统 | 提供整个路由的运行环境 |
|
||
| `NavLink` | 路牌 / 入口 | 用户点击后触发路径切换 |
|
||
| `Routes` | 调度中心 | 读取当前路径,决定走哪条路 |
|
||
| `Route` | 目的地 | 定义"路径 → 页面"的映射 |
|
||
| `Navigate` | 自动转弯 | 把用户重定向到另一个路径 |
|
||
|
||
### 路由流程
|
||
|
||
当用户点击左侧导航栏的"课程管理"时,背后发生了什么?
|
||
|
||
```mermaid
|
||
flowchart LR
|
||
A[用户点击 NavLink] --> B[URL 变为 /courses]
|
||
B --> C[Routes 读取当前路径]
|
||
C --> D{匹配哪个 Route?}
|
||
D -->|path=/courses| E[渲染 CoursesPage]
|
||
D -->|无匹配| F[不显示任何页面]
|
||
E --> G[页面切换完成,整页未刷新]
|
||
```
|
||
|
||
逐帧拆解:
|
||
|
||
1. `NavLink to="/courses"` 将浏览器地址变为 `/courses`
|
||
2. `Routes` 读取当前路径
|
||
3. 匹配到 `<Route path="/courses" element={<CoursesPage />} />`
|
||
4. `CoursesPage` 组件渲染
|
||
|
||
整个过程**地址栏变了、组件切换了、但页面没有刷新**。
|
||
|
||
### 实际代码中的用法
|
||
|
||
**路由声明**(`App.tsx`):
|
||
|
||
```tsx
|
||
// 根路径自动跳转到 dashboard(replace 不留后退痕迹)
|
||
<Route path="/" element={<Navigate to="/dashboard" replace />} />
|
||
|
||
// 正常路由映射
|
||
<Route path="/dashboard" element={<DashboardPage />} />
|
||
<Route path="/courses" element={<CoursesPage />} />
|
||
```
|
||
|
||
**导航链接**:
|
||
|
||
```tsx
|
||
<NavLink to="/courses">课程管理</NavLink>
|
||
```
|
||
|
||
**解读**:`<Route path="/" element={<Navigate to="/dashboard" replace />} />` 表示当用户访问根路径 `/` 时,自动跳转到 `/dashboard`。`replace` 参数会让浏览器用新路径替换当前历史记录,这样用户按"后退"按钮不会回到登录页。
|
||
|
||
### 为什么不能去掉 `<BrowserRouter>`?
|
||
|
||
路由组件需要"上下文"才知道当前路径是什么、该往哪里跳。如果去掉 `<BrowserRouter>`:
|
||
|
||
- `NavLink` 不知道点击后该跳到哪里
|
||
- `Route` / `Routes` 不知道当前路径是什么
|
||
- `Navigate` 无法执行跳转
|
||
|
||
它们会直接报错。
|
||
|
||
> 💡 **想一想**:如果我们不用 `<BrowserRouter>`,还有哪些方式能让路由组件知道当前路径?(提示:URL 地址栏本身就是公开的,React Router 还提供了 `HashRouter` 和 `MemoryRouter`,它们分别适用于什么场景?)
|
||
|
||
---
|
||
|
||
## 关联笔记
|
||
|
||
- [[金山办公作业/Week06/登录安全流程]] - 用户认证与 JWT Token
|
||
- [[Outlet]] - 嵌套路由的出口 / 占位符
|