This repository has been archived on 2026-05-19. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
obsidian/金山办公作业/Week06/Outlet.md
T

136 lines
3.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
tags: [frontend, react, react-router, routing, nested-routes]
create time: 2026-04-25 15:30
---
# Outlet:嵌套路由的出口 / 占位符
## 概述
`Outlet` 是 React Router 中用于"嵌套路由占位"的组件,负责在父组件布局中预留一个位置,由子路由匹配到的组件来渲染。
## 正文
### 为什么要用 Outlet
假设有这样的路由结构:
- `/dashboard`
- `/courses`
- `/students`
它们都共享同一个外层布局——左侧导航栏、顶部标题栏、主内容区域。如果不使用 `Outlet`,每个页面组件都要重复写一遍布局结构。
有了 `Outlet`,可以把公共布局抽到一个**父路由组件**中,子页面内容自动渲染到 `Outlet` 位置,不需要重复代码。
> 💡 **想一想**:如果每个子页面都自己写侧边栏和顶栏,当侧边栏需要修改时,需要改几个文件?用了 `Outlet` 后又需要改几个?
### 路由结构对比
```mermaid
graph TB
subgraph "不使用 Outlet"
A1[LoginPage] --> B1[浏览器]
A2[DashboardPage] --> B1
A3[CoursesPage] --> B1
A4[StudentsPage] --> B1
end
subgraph "使用 Outlet"
Layout[Layout 父组件<br/>header + aside + Outlet] --> B2[浏览器]
subgraph "Layout 内部"
H[<header>]
S[<aside>]
O[<Outlet />]
H --> O
S --> O
end
O --> C1[DashboardPage]
O --> C2[CoursesPage]
O --> C3[StudentsPage]
end
```
左边:每个页面独立渲染,布局代码重复。右边:父组件统一管理布局,子页面只关心自己的内容。
### 布局组件实现
```tsx
import { Outlet } from 'react-router-dom'
function Layout() {
return (
<div className="app-layout">
<header>顶部栏</header> {/* 固定不变 */}
<aside>侧边栏</aside> {/* 固定不变 */}
<main>
<Outlet /> {/* 子路由内容渲染到这里 */}
</main>
</div>
)
}
```
**解读**:`Layout` 组件中,`<header>` 和 `<aside>` 是固定结构,始终显示;`<Outlet />` 是一个"占位符",React Router 会自动把当前路径匹配到的子组件渲染到它的位置。
### 路由声明方式
**JSX 声明式写法**:
```tsx
<Route element={<Layout />}> {/* 父路由:负责布局 */}
<Route path="/dashboard" element={<DashboardPage />} />
<Route path="/courses" element={<CoursesPage />} />
<Route path="/students" element={<StudentsPage />} />
</Route>
```
**配置文件写法**:
```ts
const router = createBrowserRouter([
{
path: '/',
element: <Layout />, {/* 父路由组件 */}
children: [ {/* 子路由列表 */}
{ path: 'dashboard', element: <DashboardPage /> },
{ path: 'courses', element: <CoursesPage /> },
{ path: 'students', element: <StudentsPage /> },
],
},
])
```
两种方式等价,选择哪种取决于项目规模和个人偏好。
### Routes vs Outlet
| 特性 | `<Routes>` | `<Outlet />` |
|------|-----------|-------------|
| 职责 | 匹配路径,决定显示哪个组件 | 在父组件中预留子组件渲染位置 |
| 在哪用 | 任意需要路由判断的地方 | 只在父路由组件内部 |
| 类比 | 调度中心 | 出口 / 窗口 |
```tsx
// Routes:路径 → 组件 的映射表
<Routes>
<Route path="/courses" element={<CoursesPage />} />
</Routes>
// Outlet:父组件留给子组件的"位置"
function Layout() {
return (
<div>
<Header />
<Outlet /> {/* 子组件会出现在这里 */}
</div>
)
}
```
> 💡 **想一想**:如果父路由组件里没有放 `<Outlet />`,子路由对应的页面还能显示出来吗?(不能,子组件找不到挂载点,不会被渲染到页面上。)
## 关联笔记
- [[路由原理]] - BrowserRouter、Routes、Route 等核心路由组件