在这里插入图片描述

前端新人速通指南: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 来回跳。
要点背诵

  1. BrowserRouter 只能有一个,且放在最外层。
  2. Routes 里“先匹配先渲染”,想只渲染一个就用它,别用旧版的 Switch
  3. 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 接收对象时会替换整个查询串,想保留其它字段需自己合并。
  • 想保持“同步”状态,又不想手动拼字符串?useSearchParamsuseState + 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 里用 styleclassName 回调都能拿到 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 做成骨架屏,用户感知更丝滑。
  • 路由级别 + 组件级别双管齐下,打包产物按路由自动拆包,配合 vitedynamic 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

  1. React DevTools → Components → 搜索 Router,能看到当前 locationmatch 等上下文。
  2. 浏览器地址栏直接敲 #__debug(部分脚手架支持),可开启 Router 日志。
  3. 给每个 Route 传一个 key={location.pathname},强制重渲染,排查“组件复用导致状态残留”问题。

性能陷阱:路由很轻,业务可能很重

  • useEffect 里根据参数拉数据,一定加 cleanupAbortController,避免“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等工具

吾辈才疏学浅,摹写之作,恐有瑕疵。望诸君海涵赐教。望轻喷,嘤嘤嘤

非常期待和您一起在这个小小的网络世界里共同探索、学习和成长。愿斯文对汝有所裨益,纵其简陋未及渊博,亦足以略尽绵薄之力。倘若尚存阙漏,敬请不吝斧正,俾便精进!

在这里插入图片描述

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐