前端新人速通指南:React Router入门到实战(附避坑技巧)

前端新人速通指南:React Router入门到实战(附避坑技巧)
前端新人速通指南:React Router入门到实战(附避坑技巧)
别再让页面跳转难倒你
路由是个啥?——先讲个段子暖场
如果你第一次写 React,大概率干过这么一件蠢事:
// 千万别抄,这是反面教材
function App() {
const [page, setPage] = useState('home');
return (
<div>
<button onClick={() => setPage('about')}>点我跳到关于页</button>
{page === 'home' && <Home />}
{page === 'about' && <About />}
</div>
);
}
写完还沾沾自喜:嘿,我这是“单页应用”!——结果刷新一下,页面打回原型,URL 纹丝不动,用户一脸懵:我刚才在哪儿?
React Router 就是来拯救这种“伪路由”的。它让 URL 与 UI 保持同步,刷新不丢状态,后退前进按钮不再失灵,SEO 也能假装自己是个“多页站”。一句话:没有 Router 的 React,就像没有灵魂的重庆火锅——只剩辣,没有香。
先把锅烧热:环境搭好,包装好
# 用 vite 起的项目,也一样通用
npm i react-router-dom@6
# 如果你喜欢 ts
npm i -D @types/react-router-dom
装完别急着写,先确认 package.json 里版本号前缀是 6,下文所有代码默认 v6 语法。老项目还在用 v5?建议升,实在升不了,文末有“v5 逃生舱”。
第一道菜:Hello Router——让页面动起来
// main.jsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import App from './App';
import './index.css';
ReactDOM.createRoot(document.getElementById('root')).render(
<BrowserRouter> {/* ① 整个应用包起来,路由才能感知浏览器地址 */}
<App />
</BrowserRouter>
);
// App.jsx
import { Routes, Route, Link, Outlet } from 'react-router-dom';
function Layout() {
return (
<div>
<nav style={{ borderBottom: '1px solid #ccc', padding: 8 }}>
<Link to="/">首页</Link> | <Link to="/about">关于</Link> |{' '}
<Link to="/dashboard">后台</Link>
</nav>
<Outlet /> {/* ② 子路由出口,相当于 Vue 的 <router-view> */}
</div>
);
}
function Home() { return <h2>🏠 首页</h2>; }
function About() { return <h2>🍺 关于</h2>; }
function Dash() { return <h2>🚀 后台</h2>; }
export default function App() {
return (
<Routes>
<Route path="/" element={<Layout />}>
<Route index element={<Home />} /> {/* 默认子路由 */}
<Route path="about" element={<About />} />
<Route path="dashboard" element={<Dash />} />
</Route>
</Routes>
);
}
三分钟,纯函数组件,零状态管理,URL 就能 /about、/dashboard 来回跳。
要点背诵:
BrowserRouter只能有一个,且放在最外层。Routes里“先匹配先渲染”,想只渲染一个就用它,别用旧版的Switch。index属性表示“当父路由被精确访问时”渲染谁,避免空白。
动态参数:把 URL 当 API 用
实际业务里,商品详情、文章页、用户主页,全靠 ID 走天下。
路由写法:
<Route path="article/:id" element={<Article />} />
组件里取参:
import { useParams, useNavigate } from 'react-router-dom';
function Article() {
const { id } = useParams(); // ① 拿到 :id
const nav = useNavigate(); // ② 编程式导航
const goRandom = () => {
const randomId = Math.floor(Math.random() * 100);
nav(`/article/${randomId}`); // ③ 跳别的文章
};
return (
<div>
<h3>文章编号:{id}</h3>
<button onClick={goRandom}>随机来一篇</button>
</div>
);
}
避坑提示:
useParams()返回的是字符串,即使你在路由写:id(\d+)也只起正则校验作用,不会自动转 Number。- 路由写法区分大小写,
/article/123与/Article/123是两码事,服务器部署同理。
查询字符串?Router 也帮你捞
useSearchParams 这个钩子堪比 URLSearchParams 的“React 版”,读写都顺手:
import { useSearchParams } from 'react-router-dom';
function Search() {
const [search, setSearch] = useSearchParams();
const keyword = search.get('q') || ''; // ① 读
const page = Number(search.get('page') || 1);
const goNext = () => {
setSearch({ q: keyword, page: page + 1 }); // ② 写,自动触发刷新
};
return (
<div>
<p>
当前关键词:<b>{keyword}</b>,第 {page} 页
</p>
<button onClick={goNext}>下一页</button>
</div>
);
}
注意:
setSearch接收对象时会替换整个查询串,想保留其它字段需自己合并。- 想保持“同步”状态,又不想手动拼字符串?
useSearchParams比useState+useEffect香多了。
嵌套路由:后台系统的“大杀器”
想象一个典型后台:左侧菜单 + 顶部 Tags + 右侧主内容,全靠路由驱动。
目录结构:
src/
├─ pages/
│ ├─ Dashboard/
│ │ ├─ Index.jsx
│ │ ├─ User.jsx
│ │ ├─ Setting.jsx
路由定义:
<Route path="dashboard" element={<DashboardLayout />}>
<Route index element={<DashboardIndex />} />
<Route path="user" element={<User />} />
<Route path="setting" element={<Setting />} />
</Route>
DashboardLayout.jsx:
import { NavLink, Outlet, useNavigate } from 'react-router-dom';
export default function DashboardLayout() {
const nav = useNavigate();
return (
<div style={{ display: 'flex', height: '100vh' }}>
<aside style={{ width: 200, background: '#001529', padding: 16 }}>
<NavLink to="/dashboard" end>仪表盘</NavLink>
<NavLink to="/dashboard/user">用户管理</NavLink>
<NavLink to="/dashboard/setting">系统设置</NavLink>
</aside>
<main style={{ flex: 1, padding: 24 }}>
<button onClick={() => nav(-1)}>👈 后退</button>
<Outlet /> {/* 子路由嵌套点 */}
</main>
</div>
);
}
NavLink 自带 activeClassName,v6 里用 style 或 className 回调都能拿到 isActive:
<NavLink
to="/dashboard/user"
style={({ isActive }) => ({
display: 'block',
color: isActive ? '#1890ff' : '#fff',
marginBottom: 8,
})}
>
用户管理
</NavLink>
路由守卫:权限拦截,其实就一行逻辑
官方没有“现成的” <AuthRoute>,但封装高阶组件只要 30 秒:
import { Navigate, useLocation } from 'react-router-dom';
function PrivateRoute({ children }) {
const token = localStorage.getItem('token'); // ① 取 token
const location = useLocation(); // ② 记录原始路径
if (!token) {
return <Navigate to="/login" state={{ from: location }} replace />;
}
return children;
}
使用:
<Route
path="admin"
element={
<PrivateRoute>
<Admin />
</PrivateRoute>
}
/>
登录成功后跳回原始页:
function Login() {
const nav = useNavigate();
const location = useLocation();
const from = location.state?.from?.pathname || '/';
const onLogin = () => {
localStorage.setItem('token', 'fake-token');
nav(from, { replace: true }); // ③ replace 避免回退死循环
};
return <button onClick={onLogin}>一键登录</button>;
}
懒加载:让首屏像高铁一样快
import { lazy, Suspense } from 'react';
const Heavy = lazy(() => import('../pages/Heavy'));
<Route
path="heavy"
element={
<Suspense fallback={<div>Loading...</div>}>
<Heavy />
</Suspense>
}
/>
进阶技巧:
- 把
fallback做成骨架屏,用户感知更丝滑。 - 路由级别 + 组件级别双管齐下,打包产物按路由自动拆包,配合
vite的dynamic import天生友好。
404 与重定向:给迷路的孩子指条明路
<Route path="*" element={<NotFound />} />
放在所有路由最后,兜底匹配。
如果想把旧路径 /old-user 永久转 /user,用 Navigate:
<Route path="old-user" element={<Navigate to="/user" replace />} />
生产部署刷新 404?运维大哥在等你
单页应用全靠前端路由,浏览器刷新时,服务器找不到 /about 这个真实文件,自然 404。
解决思路:把所有请求重写到 index.html,让 JS 重新接管。
- Nginx:
location / {
try_files $uri $uri/ /index.html;
}
- Vercel:
{ "routes": [{ "src": "/(.*)", "dest": "/index.html" }] }
- GitHub Pages SPA 模式:加
404.html把首页内容抄一份,官方会读取后再跳转(脏但有效)。
调试锦囊:肉眼找 Bug 不如 DevTools
- React DevTools → Components → 搜索
Router,能看到当前location、match等上下文。 - 浏览器地址栏直接敲
#__debug(部分脚手架支持),可开启 Router 日志。 - 给每个
Route传一个key={location.pathname},强制重渲染,排查“组件复用导致状态残留”问题。
性能陷阱:路由很轻,业务可能很重
- 在
useEffect里根据参数拉数据,一定加cleanup或AbortController,避免“race condition”。 - 路由切换时滚动条停留在底部?写一个通用
ScrollToTop:
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
export default function ScrollToTop() {
const { pathname } = useLocation();
useEffect(() => {
window.scrollTo(0, 0);
}, [pathname]);
return null;
}
在 App.jsx 里塞一行 <ScrollToTop /> 即可。
v5 老项目逃生舱:快速对照表
| v5 写法 | v6 等价 |
|---|---|
<Switch> |
<Routes> |
<Route exact path> |
去掉 exact,路径按顺序匹配 |
<Route component={Home}> |
<Route element={<Home />}> |
useHistory() |
useNavigate() |
history.push() |
navigate() |
withRouter |
不再需要,钩子直接取 |
彩蛋:一行代码实现“面包屑”
import { useMatches } from 'react-router-dom';
function Breadcrumb() {
const matches = useMatches(); // 返回匹配到的路由数组
return (
<div>
{matches
.filter((match) => match.handle?.crumb)
.map((match, index) => (
<span key={index}>{match.handle.crumb} / </span>
))}
</div>
);
}
在路由里埋点:
<Route
path="user"
element={<User />}
handle={{ crumb: '用户管理' }}
/>
结语(不是客套话)
Router 学得好,单页应用才是“应用”,否则只是“单页”——用户一刷新就 404,老板一刷新就找你。
把本文代码抄过去,跑通、玩烂、改坏、再修好了,你就从“页面仔”正式升级为“路由仔”。
下一站,服务端渲染 + Remix,咱们江湖再见!
欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。
推荐:DTcode7的博客首页。
一个做过前端开发的产品经理,经历过睿智产品的折磨导致脱发之后,励志要翻身农奴把歌唱,一边打入敌人内部一边持续提升自己,为我们广大开发同胞谋福祉,坚决抵制睿智产品折磨我们码农兄弟!
| 专栏系列(点击解锁) | 学习路线(点击解锁) | 知识定位 |
|---|---|---|
| 《微信小程序相关博客》 | 持续更新中~ | 结合微信官方原生框架、uniapp等小程序框架,记录请求、封装、tabbar、UI组件的学习记录和使用技巧等 |
| 《AIGC相关博客》 | 持续更新中~ | AIGC、AI生产力工具的介绍,例如stable diffusion这种的AI绘画工具安装、使用、技巧等总结 |
| 《HTML网站开发相关》 | 《前端基础入门三大核心之html相关博客》 | 前端基础入门三大核心之html板块的内容,入坑前端或者辅助学习的必看知识 |
| 《前端基础入门三大核心之JS相关博客》 | 前端JS是JavaScript语言在网页开发中的应用,负责实现交互效果和动态内容。它与HTML和CSS并称前端三剑客,共同构建用户界面。 通过操作DOM元素、响应事件、发起网络请求等,JS使页面能够响应用户行为,实现数据动态展示和页面流畅跳转,是现代Web开发的核心 |
|
| 《前端基础入门三大核心之CSS相关博客》 | 介绍前端开发中遇到的CSS疑问和各种奇妙的CSS语法,同时收集精美的CSS效果代码,用来丰富你的web网页 | |
| 《canvas绘图相关博客》 | Canvas是HTML5中用于绘制图形的元素,通过JavaScript及其提供的绘图API,开发者可以在网页上绘制出各种复杂的图形、动画和图像效果。Canvas提供了高度的灵活性和控制力,使得前端绘图技术更加丰富和多样化 | |
| 《Vue实战相关博客》 | 持续更新中~ | 详细总结了常用UI库elementUI的使用技巧以及Vue的学习之旅 |
| 《python相关博客》 | 持续更新中~ | Python,简洁易学的编程语言,强大到足以应对各种应用场景,是编程新手的理想选择,也是专业人士的得力工具 |
| 《sql数据库相关博客》 | 持续更新中~ | SQL数据库:高效管理数据的利器,学会SQL,轻松驾驭结构化数据,解锁数据分析与挖掘的无限可能 |
| 《算法系列相关博客》 | 持续更新中~ | 算法与数据结构学习总结,通过JS来编写处理复杂有趣的算法问题,提升你的技术思维 |
| 《IT信息技术相关博客》 | 持续更新中~ | 作为信息化人员所需要掌握的底层技术,涉及软件开发、网络建设、系统维护等领域的知识 |
| 《信息化人员基础技能知识相关博客》 | 无论你是开发、产品、实施、经理,只要是从事信息化相关行业的人员,都应该掌握这些信息化的基础知识,可以不精通但是一定要了解,避免日常工作中贻笑大方 | |
| 《信息化技能面试宝典相关博客》 | 涉及信息化相关工作基础知识和面试技巧,提升自我能力与面试通过率,扩展知识面 | |
| 《前端开发习惯与小技巧相关博客》 | 持续更新中~ | 罗列常用的开发工具使用技巧,如 Vscode快捷键操作、Git、CMD、游览器控制台等 |
| 《photoshop相关博客》 | 持续更新中~ | 基础的PS学习记录,含括PPI与DPI、物理像素dp、逻辑像素dip、矢量图和位图以及帧动画等的学习总结 |
| 日常开发&办公&生产【实用工具】分享相关博客》 | 持续更新中~ | 分享介绍各种开发中、工作中、个人生产以及学习上的工具,丰富阅历,给大家提供处理事情的更多角度,学习了解更多的便利工具,如Fiddler抓包、办公快捷键、虚拟机VMware等工具 |
吾辈才疏学浅,摹写之作,恐有瑕疵。望诸君海涵赐教。望轻喷,嘤嘤嘤
非常期待和您一起在这个小小的网络世界里共同探索、学习和成长。愿斯文对汝有所裨益,纵其简陋未及渊博,亦足以略尽绵薄之力。倘若尚存阙漏,敬请不吝斧正,俾便精进!

更多推荐


所有评论(0)