一、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指定对应组件
3. Link - 无刷新导航
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.路由的严格匹配与模糊匹配
- 默认使用的是模糊匹配(简单记:【输入的路径】必须包含要【匹配的路径】,且顺序要一致)
- 开启严格匹配:
<Route exact={true} path="/about" component={About}/>
- 严格匹配不要随便开启,需要再开,有些时候开启会导致无法继续匹配二级路由
7.Redirect的使用(兜底)
- 一般写在所有路由注册的最下方,当所有路由都无法匹配时,跳转到Redirect指定的路由.
- 具体编码:
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>
</li>
<li>
<a href="/message2">message002</a>
</li>
<li>
<a href="/message/3">message003</a>
</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),代码重复率高
○ 易遗漏:可能忘记处理异步校验的异常,导致页面卡死