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

一、React Router 的基本概念

1. 什么是 React Router?

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

2. 主要特性

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

二、核心组件实战解析

1. BrowserRouter - 路由系统的基石

jsx 复制代码
import { BrowserRouter } from 'react-router-dom';

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

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

2. Route - 路由规则定义器

jsx 复制代码
<Switch>
  <Route exact path="/home" component={Home} />  //exact  严格匹配
  <Route path="/about" component={About} />
</Switch>

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

jsx 复制代码
<Link to="/">首页</Link>
<Link to="/about">关于</Link>

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

4. Switch - 路由匹配开关

jsx 复制代码
//可提高路由匹配效率
<Switch>
  <Route path="/home" component={Home} />
  <Route path="/about" component={About} />
</Switch>

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

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

1. 安装React Router

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

npm install react-router-dom

2.创建路由组件

pages/Home/index.jsx

jsx 复制代码
import React, { Component } from 'react'

export default class About extends Component {
	render() {
		// console.log('About组件收到的props是',this.props);
		return (
			<h3>我是About的内容</h3>
		)
	}
}

pages/About/index.jsx

jsx 复制代码
import React, { Component } from 'react'

export default class Home extends Component {
	render() {
		return (
			<h3>我是Home的内容</h3>
		)
	}
}

3.创建一般组件

components/Header

jsx 复制代码
import React, { Component } from 'react'

export default class Header extends Component {
	render() {
		// console.log('Header组件收到的props是',this.props);
		return (
			<div className="page-header"><h2>React Router Demo</h2></div>
		)
	}
}

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

components/MyNavLink

jsx 复制代码
import React, { Component } from 'react'
import {NavLink} from 'react-router-dom'

export default class MyNavLink extends Component {
	render() {
		// console.log(this.props);
		return (
			<NavLink activeClassName="atguigu" className="list-group-item" {...this.props}/>
		)
	}
}

5.配置路由

App.jsx

jsx 复制代码
import React, { Component } from 'react'
import { Route, Switch, Redirect } from 'react-router-dom'
import Home from './pages/Home' //Home是路由组件
import About from './pages/About' //About是路由组件
import Header from './components/Header' //Header是一般组件
import MyNavLink from './components/MyNavLink'

export default class App extends Component {
  render() {
    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">

              {/* 原生html中,靠<a>跳转不同的页面 */}
              {/* <a className="list-group-item" href="./about.html">About</a>
              <a className="list-group-item active" href="./home.html">Home</a> */}

              {/* 在React中靠路由链接实现切换组件--编写路由链接 */}
              <MyNavLink to="/about">About</MyNavLink>
              <MyNavLink to="/home">Home</MyNavLink>
            </div>
          </div>
          <div className="col-xs-6">
            <div className="panel">
              <div className="panel-body">
                {/* 注册路由 */}
                <Switch>
                  <Route path="/about" component={About} />
                  <Route path="/home" component={Home} />
                  <Redirect to="/about" />
                </Switch>
              </div>
            </div>
          </div>
        </div>
      </div>
    )
  }
}

入口文件:index.js

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代码

效果展示:

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

  1. 默认使用的是模糊匹配(简单记:【输入的路径】必须包含要【匹配的路径】,且顺序要一致)
  2. 开启严格匹配:<Route exact={true} path="/about" component={About}/>
  3. 严格匹配不要随便开启,需要再开,有些时候开启会导致无法继续匹配二级路由

7.Redirect的使用(兜底)

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

四、速通嵌套路由

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

Messages/index.jsx

jsx 复制代码
import React, { Component } from 'react'

export default class Message extends Component {
	render() {
	   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>
	   )
	}
}

News/index.jsx

jsx 复制代码
import React, { Component } from 'react'

export default class News extends Component {
	render() {
	   return (
		<ul>
	           <li>news001</li>
		   <li>news002</li>
		   <li>news003</li>
		</ul>
	   )
   }
}

在Home组件下注册子路由

jsx 复制代码
import React, { Component } from 'react'
import MyNavLink from '../../components/MyNavLink'
import {Route,Switch,Redirect} from 'react-router-dom'
import News from './News'
import Message from './Message'

export default class Home extends Component {
	render() {
	   return (
		<div>
		   <h3>我是Home的内容</h3>
		   <div>
		     <ul className="nav nav-tabs">
			<li>
			  <MyNavLink to="/home/news">News</MyNavLink>
			</li>
			<li>
			  <MyNavLink to="/home/message">Message</MyNavLink>
			</li>
		     </ul>
		     {/* 注册路由 */}
		     <Switch>
			<Route path="/home/news" component={News}/>
			<Route path="/home/message" component={Message}/>
			<Redirect to="/home/news"/>
		     </Switch>
		   </div>
		</div>
	   )
	}
}

效果展示:

五、React Router 的最佳实践

1. 使用 Hooks

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

jsx 复制代码
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. 延迟加载组件

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

jsx 复制代码
import { lazy, Suspense } from 'react';

const Home = lazy(() => import('./Home'));
const About = lazy(() => import('./About'));

function App() {
  return (
    <Router>
      <Suspense fallback={<div>加载中...</div>}>
        <Switch>
          <Route exact path="/" component={Home} />
          <Route path="/about" component={About} />
        </Switch>
      </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),代码重复率高

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

相关推荐
菜包eo2 分钟前
如何设置直播间的观看门槛,让直播间安全有效地运行?
前端·安全·音视频
烛阴30 分钟前
JavaScript函数参数完全指南:从基础到高级技巧,一网打尽!
前端·javascript
chao_7891 小时前
frame 与新窗口切换操作【selenium 】
前端·javascript·css·selenium·测试工具·自动化·html
天蓝色的鱼鱼2 小时前
从零实现浏览器摄像头控制与视频录制:基于原生 JavaScript 的完整指南
前端·javascript
三原2 小时前
7000块帮朋友做了2个小程序加一个后台管理系统,值不值?
前端·vue.js·微信小程序
popoxf2 小时前
在新版本的微信开发者工具中使用npm包
前端·npm·node.js
爱编程的喵3 小时前
React Router Dom 初步:从传统路由到现代前端导航
前端·react.js
每天吃饭的羊3 小时前
react中为啥使用剪头函数
前端·javascript·react.js
Nicholas683 小时前
Flutter帧定义与60-120FPS机制
前端
多啦C梦a3 小时前
【适合小白篇】什么是 SPA?前端路由到底在路由个啥?我来给你聊透!
前端·javascript·架构