前言|用一条路线理解 React Router
想象你走进一座大型图书馆,每一扇门都贴着标签,推开不同的门,就能进入不同主题的书架区域。
网页里的「点击、跳转、页面切换」本质上也是同一件事。
React 路由(react-router)正是这套门牌 + 指路系统:URL 像门牌号,组件像房间,它负责在不刷新页面的前提下,把用户带到正确的位置。
正因为有了它,传统网站那种“翻页书”式的跳转,逐渐被更丝滑的体验取代——
网址在变,内容在切,但页面本身没有重载。
在下面的示例中,我们会从应用的入口开始,一步步走到登录页、主页,再进入具体的功能页面。
随着路径的变化,对应的组件会被准确地渲染出来,你可以边看边敲,直接跑起来。
在开始动手之前,请先在项目中安装官方包 react-router-dom(本文示例基于 v6+)。完成安装后,就可以在组件中使用路由相关能力来控制页面的跳转与渲染。
# 安装 React Router(v6+)
npm install react-router-dom
# 检查是否安装成功
npm list react-router-dom
如果你想进一步了解路由的设计理念和完整用法,可以参考官方文档👇:
一、路由的作用:URL 与组件的映射关系
如果用一句话概括路由的作用,那就是:
把 URL 映射到组件,并控制页面切换的过程。
有了路由,我们才能用清晰、有意义的 URL 来表示不同页面,也才能在页面切换时避免刷新,让状态得以保留。同时,像登录成功后的自动跳转、嵌套路由、404 页面这些常见需求,也都依赖路由来完成。
当你理解了这一点之后,路由本身就不再神秘了。
真正需要思考的其实是:页面结构该如何组织。
二、路由分层讲解
2.1 一级路由:应用的整体入口
一级路由决定的是:你现在站在这栋建筑的哪一层、哪个区域。
例如 /login 表示登录页,/home 表示后台主页。
这些「大方向」的路径,通常统一写在 App.jsx 中,由 BrowserRouter + Routes 来集中管理。
// src/App.jsx
import React from 'react'
// 下面引入的路由相关组件后面都会有解释
import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom'
import Login from './pages/Login'
import Home from './pages/Home'
import Class from './pages/Class'
import LeetCode from './pages/LeetCode'
import NotFound from './pages/NotFound'
export default function App() {
return (
// BrowserRouter:路由总容器,监听地址栏变化
// 如果项目部署在子路径下,可配置 basename="/app"
<BrowserRouter>
<Routes>
<Route path="/" element={<Navigate to="/login" replace />} />
{/* 一级路由:登录页 */}
<Route path="/login" element={<Login />} />
{/* 一级路由:主页(作为二级路由的父容器,后面专门讲)*/}
<Route path="/home" element={<Home />}>
{/* index 路由:访问 /home 时的默认页面 */}
<Route index element={<Class />} />
{/* 二级路由👇 */}
<Route path="class" element={<Class />} />
<Route path="leetcode" element={<LeetCode />} />
</Route>
{/* 所有未匹配到的路径,统一显示 404 页面 */}
<Route path="*" element={<NotFound />} />
</Routes>
</BrowserRouter>
)
}
在这段代码中,涉及了几个一级路由里最核心的角色:
BrowserRouter 是整个应用的最外层容器,它负责接管浏览器地址栏的变化,并把 URL 的变化同步给 React;没有它,后面的路由规则是无法生效的。
Routes 可以理解为一个「路由出口」,它会根据当前的地址,从内部的规则中挑选出最匹配的一条进行渲染。
而每一个 Route,本质上都是一条「路径 → 组件」的映射规则:当 URL 命中某个路径时,对应的组件就会被渲染出来。
至于 Navigate,则更像是一个路由层面的“转向标志”。在这里,我们用它把根路径 / 自动引导到 /login,让用户一进入应用就落在登录页,而不是停在一个空白页面。
App.jsx就像整栋楼的楼层分布图。 用户一进来先决定去哪一层,再在这一层里继续活动。
2.2 二级路由:固定布局下的内容切换
进入 /home 之后,页面结构通常是相对固定的:
侧边栏、头部这些公共区域不会变,真正变化的是中间的内容区。
这正是二级路由要解决的问题:
父组件 Home 负责公共布局,而子路由(如 Class、LeetCode)只负责内容本身。
// src/pages/Home.jsx
import { Outlet, Link } from 'react-router-dom'
export default function Home() {
return (
<div style={{ display: 'flex', height: '100vh' }}>
<aside style={{ width: 200, background: '#f5f5f5', padding: 16 }}>
<h3>后台管理</h3>
<ul style={{ listStyle: 'none', padding: 0 }}>
{/* 相对路径写法,基于当前父路由 /home */}
<li><Link to="class">课程</Link></li>
<li><Link to="leetcode">算法</Link></li>
</ul>
</aside>
<main style={{ flex: 1, padding: 20 }}>
<header style={{ marginBottom: 12 }}>
<strong>Home 公共布局</strong>
</header>
{/* 二级路由内容渲染位置 */}
<Outlet />
</main>
</div>
)
}
在父路由组件中使用
to="class"这样的相对路径,而不是to="/home/class",可以降低路径耦合度。如果将来/home改名,只需要改路由配置,不必逐个修改 Link。
下面就来说明 Home.jsx 同时演示的两个重要点:Outlet(子路由渲染点) 与 Link(无刷新导航) 吧。
<Outlet />:子路由的渲染出口
它只是告诉 React —— 子路由渲染到这里。
父组件不需要关心当前显示的是哪个子页面,只负责把位置预留出来,具体显示什么内容,由当前命中的子路由决定。
Link:不刷新页面的导航方式
点击 Link 时,浏览器地址栏里的 URL 会发生变化,但页面不会刷新。
React 会根据新的路径,直接切换渲染对应的组件,就像用遥控器换台一样,内容在变,但屏幕本身没有重启。
Class / LeetCode(二级路由页面示例)
// src/pages/Class.jsx
import React from 'react'
export default function Class() {
return (
<div>
<h2>课程页面</h2>
<p>这里可以放课程列表或详情。</p>
</div>
)
}
// src/pages/LeetCode.jsx
import React from 'react'
export default function LeetCode() {
return (
<div>
<h2>算法练习</h2>
<p>这里可以放 LeetCode 刷题内容。</p>
</div>
)
}
当访问 /home/class 或 /home/leetcode 时,对应组件就会被渲染到 Home 中的 <Outlet /> 位置。
2.3 useNavigate:由代码控制的页面跳转
并不是所有页面跳转,都是用户主动点击链接完成的。
当跳转需要由程序逻辑触发时,就该 useNavigate 登场了。
// src/pages/Login.jsx
import React from 'react'
import { useNavigate } from 'react-router-dom'
export default function Login() {
const navigate = useNavigate()
function handleLogin() {
// 登录逻辑省略
//这里使用 `replace: true`,可以避免用户点击浏览器“后退”时回到登录页。
navigate('/home/class', { replace: true })
}
return (
<div style={{ padding: 20 }}>
<h2>登录页</h2>
<input placeholder="账号" />
<input placeholder="密码" type="password" />
<button onClick={handleLogin}>登录</button>
</div>
)
}
useNavigate 的核心作用可以概括为一句话:
让「代码」而不是「用户点击」来决定页面跳转。
Link只负责“点哪里跳哪里”,无法表达流程上的判断与控制;
当需要代码根据如“登录成功了没❓”这样的某个结果来决定是否跳转时,就该用 useNavigate了。
结语|路由不是难点,页面结构才是
当你把这些角色放在一起时,路由的整体结构就会变得非常清晰:
一级路由负责「站在哪一层」,二级路由负责「这一层里看什么内容」。
Outlet 提供内容的展示窗口,Link 负责无刷新切换,而 useNavigate 则让代码也能参与页面流程的控制。
React Router 本身并不复杂,真正的难点在于:你如何组织页面结构。
如果你是刚开始学习 React,这套分层思路会在未来很长一段时间里反复帮到你。
下一步不妨亲手敲一遍这套代码,让你的第一个 SPA真正“活”起来。
如果这篇文章对你有帮助,欢迎点赞 / 收藏 / 关注专栏 ❤️
后续会继续拆解 React 中那些「看起来难,其实有章法」的知识点。