别再踩坑!React Router 路由匹配、嵌套导航全解析(附避坑指南)

557 阅读6分钟

一、React Router 的基本概念

1. 什么是 React Router?

React Router 就是SPA(单页应用)的导航控制器。想象你有一个乐高城堡,每个房间(页面)都通过走廊(路由)连接。当你点击导航按钮时,城堡会"魔术般"地切换房间,而URL会自动更新。

2. 主要特性

  • 🧩 声明式路由:用组件方式配置路由规则
  • 🌳 嵌套路由:支持多层页面结构
  • 🔄 动态路由:URL中可以包含变量参数
  • 🛠️ 程序化导航:像操作数组一样控制跳转
  • 📁 浏览器历史记录:支持前进/后退按钮

二、核心组件实战解析

1. BrowserRouter - 路由系统的基石

import { BrowserRouter } from 'react-router-dom';

const Root = () => {
  return (
    <BrowserRouter>
      <App />
    </BrowserRouter>
  );
};

效果演示:包裹整个应用后,所有路由功能将被激活

2. Route - 路由规则定义器

<Routes>
  <Route path="/home" element={<Home />} />  {/* 函数式组件使用element属性 */}
  <Route path="/about" element={<About />} />
</Routes>

关键点:path指定路径,component指定对应组件

3. Link - 无刷新导航

<Link to="/">首页</Link>
<Link to="/about">关于</Link>

对比传统a标签:点击不会刷新页面,实现SPA体验

4. Switch - 路由匹配开关

// 提高路由匹配效率,v6中用Routes替代Switch
<Routes>
  <Route path="/home" element={<Home />} />
  <Route path="/about" element={<About />} />
</Routes>

作用:只渲染第一个匹配的路由,避免多组件同时显示

三、从0到1搭建路由系统

1. 安装React Router

可以使用npm或yarn安装React Router DOM

npm install react-router-dom

2.创建路由组件

pages/Home/index.jsx

const Home = () => {
  return (
    <h3>我是Home的内容</h3>
  );
};

export default Home;

pages/About/index.jsx

const About = () => {
  return (
    <h3>我是About的内容</h3>
  );
};

export default About;

3.创建一般组件

components/Header

const Header = () => {
  return (
    <div className="page-header"><h2>React Router Demo</h2></div>
  );
};

export default Header;

4.封装NavLink(实现高亮效果)

components/MyNavLink

import { NavLink } from 'react-router-dom';

const MyNavLink = (props) => {
  return (
    <NavLink 
      activeClassName="atguigu" 
      className="list-group-item" 
      {...props}
    />
  );
};

export default MyNavLink;

5.配置路由

App.jsx

import { Route, Routes, Navigate } from 'react-router-dom';
import Home from './pages/Home';
import About from './pages/About';
import Header from './components/Header';
import MyNavLink from './components/MyNavLink';

const App = () => {
  return (
    <div>
      <div className="row">
        <div className="col-xs-offset-2 col-xs-8">
          <Header />
        </div>
      </div>
      <div className="row">
        <div className="col-xs-2 col-xs-offset-2">
          <div className="list-group">
            {/* 路由链接 */}
            <MyNavLink to="/about">About</MyNavLink>
            <MyNavLink to="/home">Home</MyNavLink>
          </div>
        </div>
        <div className="col-xs-6">
          <div className="panel">
            <div className="panel-body">
              {/* 注册路由(v6中用Routes和element) */}
              <Routes>
                <Route path="/about" element={<About />} />
                <Route path="/home" element={<Home />} />
                <Route path="/" element={<Navigate to="/about" />} /> {/* 替代Redirect */}
              </Routes>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
};

export default App;

入口文件:index.js

//引入react核心库
import React from 'react'
//引入react-dom
import ReactDOM from 'react-dom/client'
//引入路由组件
import { BrowserRouter } from 'react-router-dom'
//引入App组件
import App from './App'

//渲染App组件到页面
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>
);

想要练习的jym可以私我要css代码

效果展示:

image.png

image.png

6.路由的严格匹配与模糊匹配

  1. 默认使用的是模糊匹配(简单记:【输入的路径】必须包含要【匹配的路径】,且顺序要一致)
  2. 开启严格匹配:<Route path="/about" element={<About />} exact />(v6 中 exact 属性已移除,默认严格匹配)
  3. 注意:React Router v6 中默认启用严格匹配,无需手动添加exact属性

7.Navigate 的使用(兜底,替代 v5 的 Redirect)

  1. 一般写在所有路由注册的最下方,当所有路由都无法匹配时,跳转到 Navigate 指定的路由.
  2. 具体编码:
<Routes>
  <Route path="/about" element={<About />} />
  <Route path="/home" element={<Home />} />
  <Route path="/" element={<Navigate to="/about" />} />
</Routes>

四、速通嵌套路由

嵌套路由就是在组件内部定义子路由。在上面路由系统的基础上,在Home组件下创建子路由Message和News.

Messages/index.jsx

const Message = () => {
  return (
    <div>
      <ul>
        <li>
          <a href="/message1">message001</a>&nbsp;&nbsp;
        </li>
        <li>
          <a href="/message2">message002</a>&nbsp;&nbsp;
        </li>
        <li>
          <a href="/message/3">message003</a>&nbsp;&nbsp;
        </li>
      </ul>
    </div>
  );
};

export default Message;

News/index.jsx

const News = () => {
  return (
    <ul>
      <li>news001</li>
      <li>news002</li>
      <li>news003</li>
    </ul>
  );
};

export default News;

在Home组件下注册子路由

import MyNavLink from '../../components/MyNavLink';
import { Route, Routes, Navigate } from 'react-router-dom';
import News from './News';
import Message from './Message';

const Home = () => {
  return (
    <div>
      <h3>我是Home的内容</h3>
      <div>
        <ul className="nav nav-tabs">
          <li>
            <MyNavLink to="news">News</MyNavLink> {/* 嵌套路由可省略父路径 */}
          </li>
          <li>
            <MyNavLink to="message">Message</MyNavLink>
          </li>
        </ul>
        {/* 注册子路由 */}
        <Routes>
          <Route path="news" element={<News />} /> {/* 无需写完整路径 */}
          <Route path="message" element={<Message />} />
          <Route path="" element={<Navigate to="news" />} />
        </Routes>
      </div>
    </div>
  );
};

export default Home;

效果展示:

image.png

image.png

五、React Router 的最佳实践

1. 使用 Hooks

React Router v5.1 引入了 Hooks,能够更方便地获取路由信息:

import { useParams, useHistory } from 'react-router-dom';

const User = () => {
  const { id } = useParams();
  const history = useHistory();

  return (
    <div>
      <h1>用户ID: {id}</h1>
      <button onClick={() => history.goBack()}>返回</button>
    </div>
  );
};

2. 延迟加载组件

延迟加载组件可以提高应用的加载速度:

import { lazy, Suspense } from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';

// 懒加载组件
const Home = lazy(() => import('./Home'));
const About = lazy(() => import('./About'));

const App = () => {
  return (
    <Router>
      <Suspense fallback={<div>加载中...</div>}>  {/* 加载时显示的占位内容 */}
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/about" element={<About />} />
        </Routes>
      </Suspense>
    </Router>
  );
};

总结

React Router 为 React 应用提供了强大且灵活的路由解决方案,无论是简单的单页应用,还是复杂的企业级应用,它都能很好地满足需求。通过本文的介绍,你已经了解了 React Router 的基本概念、核心组件、使用方法、进阶应用以及最佳实践。希望这些内容能够帮助你在开发 React 应用时更加高效地管理路由。

经典面试题

React Router v6的loader函数与自定义守卫组件在性能上有何差异?

回答:

⚙️ 1. 执行时机与渲染流程

(1)loader函数

○ 时机:在路由匹配后、组件渲染前执行,属于路由层拦截

○ 优势:

  • 并行加载:多个路由的loader可并行执行,避免组件层级的“请求瀑布流”(Network Waterfalls)

  • 无冗余渲染:若loader中重定向(如redirect(‘/login’)),直接中断渲染流程,不会触发组件生命周期

○ 性能影响:减少不必要的组件挂载和卸载开销,适合深层嵌套路由的权限校验

(2) 自定义守卫组件

○ 时机:在组件渲染阶段执行(如useEffect或渲染逻辑中),属于组件层拦截

○ 劣势:

  • 组件需先渲染:即使权限校验失败,守卫组件及其子组件仍会经历挂载→校验→跳转的过程,可能触发多次状态更新

  • 串行请求:若父子路由均需权限校验,请求会按层级顺序执行,延长整体加载时间

⚡ 2. 数据预加载与用户体验

(1) loader函数

○ 数据预加载:可在权限校验时同步预加载页面数据,通过useLoaderData直接传递至组件,避免二次请求

○ 用户体验优化:

  • 结合Suspense展示全局加载状态,减少页面闪烁

  • 支持缓存策略(如内存缓存),避免重复请求相同数据

(2)自定义守卫组件

○ 数据分离:权限校验与数据加载逻辑分离,需在组件内单独处理数据请求,易导致加载状态分散(如多个loading提示)

○ 白屏风险:若权限校验后需加载数据,用户可能经历“校验→跳转→数据加载→渲染”的流程,增加等待时间

🛡️ 3. 错误处理效率

(1) loader函数

○ 统一错误处理:通过errorElement集中处理loader抛出的异常(如401重定向),减少冗余代码

○ 错误边界清晰:错误仅影响当前路由子树,不影响全局布局

(2) 自定义守卫组件

○ 分散处理:需在每个守卫组件内单独处理错误(如try/catch),代码重复率高

○ 易遗漏:可能忘记处理异步校验的异常,导致页面卡死