React基础知识三 router路由全指南
现在最新版本是Router6和Router5有比较大的变化,Router5和Router4变化不大,本文以Router6的写法为主,也会对比和Router5的不同。比较全面。
安装路由
npm i react-router-dom
基本使用
有两种Router,BrowserRouter和HashRouter,选哪个都可以,这里以HashRouter为例子,BrowserRouter用法上没什么区别。
先给App组件套上一层Router,这样就可以使用路由功能了。
import { BrowserRouter, HashRouter } from "react-router-dom";root.render(<React.StrictMode><HashRouter><App /></HashRouter></React.StrictMode>
);
或者
root.render(<React.StrictMode><BrowserRouter><App /></BrowserRouter></React.StrictMode>
);
在App里面我们需要定义一个Routes组件,以及他的子组件Route,Route会将path映射成对应的组件。也就是动态加载我们的Home或者About组件。
import { Routes, Route } from "react-router-dom";
import Home from "./pages/Home";
import About from "./pages/About";function App() {return (<div className="App"><div>Header</div><div><Routes><Route path="/home" element={<Home />} /><Route path="/about" element={<About />} /></Routes></div><div>Footer</div></div>);
}export default App;
Home和About就展示文本
import React from "react";
class Home extends React.PureComponent {render() {return <div>Home Page</div>;}
}
export default Home;
在浏览器中输入路径,注意我们使用的是HashRouter,所以路径前面需要加#号。
http://localhost:3001/#/home
效果就是Home组件的内容确实被动态加载了。
使用Link组件实现点击切换
上面的代码可以使用浏览器输入路径动态的切换内容。但一般用户不会直接在浏览器输入地址。一般我们会给个导航栏让用户点击,这个功能用Link就可以很容易实现。
Link内容直接写显示的文本,以及一个to属性用于指定路径,和Route的path属性对应上就可以了。
import { Routes, Route, Link } from "react-router-dom";
import Home from "./pages/Home";
import About from "./pages/About";function App() {return (<div className="App"><div><Link to="/home">首页</Link><Link to="/about">关于</Link></div><div><Routes><Route path="/home" element={<Home />} /><Route path="/about" element={<About />} /></Routes></div><div>Footer</div></div>);
}export default App;
这样,就可以通过Link组件实现点击动态切换路由内容了。
404页面
当所有的路径都匹配不到的时候,就匹配到了*。
<Route path="/home" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/login" element={<Login />} />
<Route path="*" element={<NotFound />} />
Link的一些额外属性
- replace 是否重定向,默认false
- reloadDocument 是否出现加载文档,会导致填的数据丢失,默认false
- state 可以传值,在目标组件使用useLocation可以获取到值。
<Link to="/home" replace={false} reloadDocument={false} state={{name:"Tom"}}>
NavLink的使用(了解)
NavLink可以使链接样式有一些变化。但实际上我们是很少使用的,因为我们一般都自定义。作为了解就可以了。
当我们把Home组件的Link替换成NavLink的时候,当Home组件的NavLink被点击的时候,我们查看源代码,发现,a元素上面多了一个active的class。但NavLink被点击的时候,我们可以通过这个active class来做一些样式上的变化。
我们直接定义一个class,在组件里面引入,在点击的时候就会触发这个效果了。
.active {color: red;font-size: 20px;
}
支持下面的写法,函数参数是arguments,我们可以解构出isActive ,这是这个组件提供的。
<NavLinkto="/home"style={({ isActive }) => ({ color: isActive ? "red" : "green" })}>首页</NavLink>
也可以提供指定className的形式。
<NavLinkto="/about"className={({ isActive }) => (isActive ? "linkActive" : "")}>关于</NavLink>
这两个方式都可以简单的改变样式。
Navigate组件的使用
Navigate这个组件是用于重定向的。只要他被显示,那么组件就会被重定向。可是实现登录跳转。
我们添加一个Login组件。并做路由配置。
实现登录跳转
import { Routes, Route, Link, NavLink } from "react-router-dom";
import Home from "./pages/Home";
import About from "./pages/About";
import Login from "./pages/Login";
import "./pages/style.css";function App() {return (<div className="App"><div><NavLinkto="/home"style={({ isActive }) => ({ color: isActive ? "red" : "green" })}>首页</NavLink><NavLinkto="/about"className={({ isActive }) => (isActive ? "linkActive" : "")}>关于</NavLink><NavLinkto="/login"className={({ isActive }) => (isActive ? "linkActive" : "")}>登录</NavLink></div><div><Routes><Route path="/home" element={<Home />} /><Route path="/about" element={<About />} /><Route path="/login" element={<Login />} /></Routes></div><div>Footer</div></div>);
}export default App;
做一个条件判断,只要满足条件,我们就展示Navigate,然后组件就会跳转到指定的路由。
import React from "react";
import { Navigate } from "react-router-dom";
class Login extends React.PureComponent {state = {isLogin: false,};handleLogin() {this.setState({isLogin: true,});}render() {const { isLogin } = this.state;return (<div>Login Page{!isLogin ? (<button onClick={(e) => this.handleLogin()}>登录</button>) : (<Navigate to="/home" />)}</div>);}
}
export default Login;
路径重定向
我们可以把/重定向到Home。
<Route path="/" element={<Navigate to="/home" />} />
路由嵌套
也就是二级路由,这个是很常见的。也不难,还是比较方便的。
需要给路由定义子路由。并且在路由是/home的时候,重定向到/home/recommends,这样可以避免,第一次进来二级内容是空白的情况。
App.js
<Routes><Route path="/" element={<Navigate to="/home" />} /><Route path="/home" element={<Navigate to="/home/recommends" />} /><Route path="/home" element={<Home />}><Route path="/home/recommends" element={<HomeRecommends />}></Route><Route path="/home/hots" element={<HomeHots />}></Route></Route></Routes>
在具体的页面设置Link组件,并且放一个Outlet组件作为占位符。
<div>Home Page<div><Link to="/home/recommends">推荐</Link><Link to="/home/hots">热门</Link><Outlet /></div></div>
通过代码实现路由跳转
有时候,我们需要通过代码实现路由跳转。在router6里面,我们只能通过useNavigate这个hooks来实现代码路由跳转。用了hooks,我们只能使用函数组件。如果一定要在类组件里面实现代码路由跳转,我们只能自定义一个高阶函数withRouter(也是router6的写法)。
使用useNavigate hooks
虽然是通过代码实现路由跳转,但Route还是要手动定义的,这是跳转是通过代码实现的。
<Route path="/download" element={<Download />} /><Route path="/shop" element={<Shop />} />
我们在原来的Link后面添加下面两个元素,我们自己实现点击事件
<button onClick={(e) => handleNavigate("/download")}>下载</button><span onClick={(e) => handleNavigate("/shop")}>商城</span>
使用useNavigate来实现跳转。这样就已经实现了通过代码实现路由跳转的功能了。
function App() {//这行必须写在函数外面const navigate = useNavigate();function handleNavigate(path) {navigate(path);}return ...div...
}
使用自定义实现高阶函数withRouter
这个自定义的高阶组件,内部也是使用了useNavigate,所以是router6才可以这么用。router5是自带一个withRouter函数的,只是这个函数在router6被移除了。我们自己实现的这个只是叫withRouter这个名字,内部实现是不一样的。
因为useNavigate只能在函数组件里面使用,我们怎么在类组件里面也能用呢?
就是使用高阶函数,中间套一个函数组件,并将navigate 放到props里面,经过这个骚操作,我们就可以在类组件的props里面获取到navigate了。
import { useNavigate } from "react-router-dom";function withRouter(OriginComponent) {return function (props) {const navigate = useNavigate();const router = { navigate };// 套一个router对象,因为可能会封装别的操作return <OriginComponent {...props} router={router} />;};
}export default withRouter;
这样,我们就可以通过this.props获取到navigate了。
import React from "react";
import withRouter from "../hoc/withRouter";
import { Outlet } from "react-router-dom";
class Shop extends React.PureComponent {handleNavigate(path) {const { navigate } = this.props.router;navigate(path);}render() {return (<div>Shop Page<div><span onClick={(e) => this.handleNavigate("/shop/hots")}>热卖</span><span> </span><span onClick={(e) => this.handleNavigate("/shop/offsale")}>折扣</span></div><Outlet /></div>);}
}
export default withRouter(Shop);
App.js定义我们的Route。
<Route path="/shop" element={<Navigate to="/shop/hots" />} /><Route path="/shop" element={<Shop />}><Route path="/shop/hots" element={<ShopHots />} /><Route path="/shop/offsale" element={<ShopOffsale />} /></Route>
效果就是点击商城,再点击热卖或者折扣,也可以切换路由。
路由参数传递 动态路由 查询字符
参数传递和动态路由
下面的形式是动态路由的形式,我们可以根据传递的id来显示不同的页面。
<Route path="/shop/detail/:id" element={<ShopDetail />} />
和获取navigate一样,我们可以获取一个params,那么这个params怎么来的呢?我们需要在withRouter里面封装到props里面。
import React from "react";
import withRouter from "../hoc/withRouter";
class ShopDetail extends React.PureComponent {render() {const { params } = this.props.router;console.log(params);return <div>ShopDetail id:{params.id}</div>;}
}
export default withRouter(ShopDetail);
这样,我们就成功实现把params 封装到props里面了。
import { useNavigate, useParams } from "react-router-dom";function withRouter(OriginComponent) {return function (props) {const navigate = useNavigate();const params = useParams();const router = { navigate, params };return <OriginComponent {...props} router={router} />;};
}export default withRouter;
我们的事件发起源是下面这个地方,通过navigate把含有id的路径传递给目标路由。
import React from "react";
import withRouter from "./../hoc/withRouter";
class ShopHots extends React.PureComponent {state = {goods: [{ id: "001", name: "商品1" },{ id: "002", name: "商品2" },{ id: "003", name: "商品3" },],};navigateToDetail(id) {console.log(id);const { navigate } = this.props.router;navigate("/shop/detail/" + id);}render() {const { goods } = this.state;return (<div>商店热门<div><ul>{goods.map((item, index) => {return (<li onClick={(e) => this.navigateToDetail(item.id)} key={index}>{item.name}</li>);})}</ul></div></div>);}
}
export default withRouter(ShopHots);
最后实现的效果如下:
我们点击具体的商品,跳转到商品详情页。
查询字符
我们给关于这个Link传点参数。
<NavLinkto="/about?user=tom&age=18"className={({ isActive }) => (isActive ? "linkActive" : "")}>关于</NavLink>
在withRouter里面通过useSearchParams把query放到props里面。
import {useLocation,useNavigate,useParams,useSearchParams,
} from "react-router-dom";function withRouter(OriginComponent) {return function (props) {const navigate = useNavigate();const params = useParams();// const location = useLocation();// console.log("location:", location);const [searchParams] = useSearchParams();const query = Object.fromEntries(searchParams);const router = { navigate, params, query };return <OriginComponent {...props} router={router} />;};
}export default withRouter;
最后,在目标页面就可以通过props获取到query了。
import React from "react";
import withRouter from "../hoc/withRouter";class About extends React.PureComponent {render() {const { query } = this.props.router;return <div>About Page:{query.user + " " + query.age}</div>;}
}
export default withRouter(About);
路由配置
前面写的路由是通过Route组件来实现的,router6提供了配置文件的形式,估计是参考vue的,本质上,react是把配置文件还是转成我们上面写的Route组件的形式。
现在,我们的Route是这些内容:
<div><Routes><Route path="/" element={<Navigate to="/home" />} /><Route path="/home" element={<Navigate to="/home/recommends" />} /><Route path="/home" element={<Home />}><Route path="/home/recommends" element={<HomeRecommends />}></Route><Route path="/home/hots" element={<HomeHots />}></Route></Route><Route path="/about" element={<About />} /><Route path="/login" element={<Login />} /><Route path="/download" element={<Download />} /><Route path="/shop" element={<Navigate to="/shop/hots" />} /><Route path="/shop/detail/:id" element={<ShopDetail />} /><Route path="/shop" element={<Shop />}><Route path="/shop/hots" element={<ShopHots />} /><Route path="/shop/offsale" element={<ShopOffsale />} /></Route><Route path="*" element={<NotFound />} /></Routes></div>
修改成下面的样子:
import { Navigate } from "react-router-dom";
import Home from "../pages/Home";
import About from "../pages/About";
import Login from "../pages/Login";
import NotFound from "../pages/NotFound";
import HomeHots from "../pages/HomeHots";
import HomeRecommends from "../pages/HomeRecommends";
import Download from "../pages/Download";
import Shop from "../pages/Shop";
import ShopHots from "../pages/ShopHots";
import ShopOffsale from "../pages/ShopOffsale";
import ShopDetail from "../pages/ShopDetail";
const routes = [{path: "/",element: <Navigate to="/home" />,},{path: "/home",element: <Navigate to="/home/recommends" />,},{path: "/home",element: <Home />,children: [{path: "/home/recommends",element: <HomeRecommends />,},{path: "/home/hots",element: <HomeHots />,},],},{path: "/about",element: <About />,},{path: "/login",element: <Login />,},{path: "/download",element: <Download />,},{path: "/shop",element: <Navigate to="/shop/hots" />,},{path: "/shop/detail/:id",element: <ShopDetail />,},{path: "/shop",element: <Shop />,children: [{path: "/shop/hots",element: <ShopHots />,},{path: "/shop/offsale",element: <ShopOffsale />,},],},{path: "*",element: <NotFound />,},
];export default routes;
最后原来的位置提供useRoutes把routes传进去,react就帮我们把配置文件解析好转成Route的形式了。
<div>{useRoutes(routes)}</div>
懒加载分包和Suspense
在不使用懒加载的情况下,打包后的js内容全部打包到main这个js文件里面。
我们可以通过下面的语句,对页面进行懒加载。import是webpack提供的特性。
const About = React.lazy(() => import("../pages/About"));
const Home = React.lazy(() => import("../pages/Home"));
在使用懒加载后,多出的js文件就是懒加载分包的js文件。
通常情况下,在加载了懒加载之后,页面会直接崩溃的,但新版本react好像没有这个问题了,如果崩溃,需要添加Suspense,可以有loading的效果,当然fallback可以写空字符串,用户就不会察觉到有loading。
<Suspense fallback="loading..."><HashRouter><App /></HashRouter></Suspense>
相关文章:

React基础知识三 router路由全指南
现在最新版本是Router6和Router5有比较大的变化,Router5和Router4变化不大,本文以Router6的写法为主,也会对比和Router5的不同。比较全面。 安装路由 npm i react-router-dom基本使用 有两种Router,BrowserRouter和HashRouter&…...

[VUE]框架网页开发02-如何打包Vue.js框架网页并在服务器中通过Tomcat启动
在现代Web开发中,Vue.js已经成为前端开发的热门选择之一。然而,将Vue.js项目打包并部署到生产环境可能会让一些开发者感到困惑。本文将详细介绍如何将Vue.js项目打包,并通过Tomcat服务器启动运行。 1. 准备工作 确保你的项目能够正常运行,项…...
k8s Quality of Service
文章目录 QoS 分类规则QoS 类别影响创建 QoS 分类的案例1. Guaranteed QoS 示例示例 YAML 文件: 2. Burstable QoS 示例示例 YAML 文件: 3. BestEffort QoS 示例示例 YAML 文件: 4. 混合 QoS 示例(多个容器)示例 YAML …...

顶刊算法 | 鱼鹰算法OOA-BiTCN-BiGRU-Attention多输入单输出回归预测(Maltab)
顶刊算法 | 鱼鹰算法OOA-BiTCN-BiGRU-Attention多输入单输出回归预测(Maltab) 目录 顶刊算法 | 鱼鹰算法OOA-BiTCN-BiGRU-Attention多输入单输出回归预测(Maltab)效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实…...
什么语言适合做 Serverless 开发?
随着云计算的普及,**无服务器架构(Serverless Architecture)**成为一种流行的开发模式,它使得开发者无需管理服务器基础设施,专注于编写应用逻辑。无服务器架构通常按需提供计算资源,能够灵活地扩展&#x…...
使用OpenCV和卡尔曼滤波器进行实时活体检测
引言 在现代计算机视觉应用中,实时检测和跟踪物体是一项重要的任务。本文将详细介绍如何使用OpenCV库和卡尔曼滤波器来实现一个实时的活体检测系统。该系统能够通过摄像头捕捉视频流,并使用YOLOv3模型来检测目标对象(例如人)&…...
【25春招前端八股文】——JS数据类型检测方式
检测数据类型 # typeof 总结:数组、对象、null都会被判断为object,其他判断都正确的类型。 可以检测基本数据类型null会检测为Object,因为null也是一个空的引用对象复杂数据类型只能检测function和Object 情况说明: 数组&#x…...
Kafka的学习路径规划
目录标题 1. 记(记忆力)Kafka核心概念Kafka关键配置 2. 懂(理解力)Kafka工作原理Kafka核心功能Kafka架构设计 3. 网(知识网络)技术栈整合用例和场景 4. 拓(全面拓展)学习材料多样化内…...
linux模拟试题
Linux 基础阶段考试笔试模拟试卷 审核人:王旺旺 一.填空题(每题 1 分,共 30 分) 1.验证 httpd 服务是否启动的命令是_______ 答:systemctl status httpd 或 netstat -anptl 或 ss -anpt 2.将目录 xxhf 下所有文件的所属组改为 user1 的命令是_______ 答:chown -R ,user1 …...

Qt-界面优化QSS
QSS介绍 先说下CSS: 在⽹⻚前端开发领域中, CSS 是⼀个⾄关重要的部分. 描述了⼀个⽹⻚的 "样式". 从⽽起到对⽹⻚美化的作⽤。 Qt 仿照 CSS 的模式, 引⼊了 QSS, 来对 Qt 中的控件做出样式上的设定 。 CSS的功能很强大,QSS要逊色一些&#…...

QT实战-qt各种菜单样式实现
本文主要介绍了qt普通菜单样式、带选中样式、带子菜单样式、超过一屏幕菜单样式、自定义带有滚动条的菜单样式, 先上图如下: 1.普通菜单样式 代码: m_pmenu new QMenu(this);m_pmenu->setObjectName("quoteListMenu"); qss文…...

深度学习基础03_BP算法(下)过拟合和欠拟合
目录 一、BP算法(下) 0、反向传播代码回顾 写法一: 写法二(更常用): 1、BP中的梯度下降 1.数学描述 2.传统下降方式 3.优化梯度下降方式 指数加权平均 Momentum AdaGrad RMSProp Adam(常用) 总结 二、过拟合和欠拟合 1、概念 1.过拟合 …...

web vue 滑动选择 n宫格选中 九宫格选中
页面动态布局经常性要交给客户来操作,他们按时他们的习惯在同一个屏幕内显示若干个子视图,尤其是在医学影像领域对于影像的同屏显示目视对比显的更为重要。 来看看如下的用户体验: 设计为最多支持5行6列页面展示后,右侧的布局则动…...
Spring Boot整合EasyExcel
Spring Boot整合EasyExcel主要涉及到以下几个步骤: 1.添加EasyExcel依赖到Spring Boot项目的pom.xml文件中。 2.创建数据模型类,用于映射Excel文件中的数据。 3.编写读取和写入Excel的服务。 以下是一个简单的例子: 1.添加EasyExcel依赖 …...

微软表示不会使用你的 Word、Excel 数据进行 AI 训练
微软否认使用 Microsoft 365 应用程序(包括 Word、Excel 和 PowerPoint)收集数据来训练公司人工智能 (AI) 模型的说法。 此前,Tumblr 的一篇博文声称,雷德蒙德使用“互联体验”功能抓取客户的 Word 和 Excel 数据,用…...

JavaScript(一)
1.JavaScript 基本使用 2.JavaScript简单事件 3.JavaScript修改样式 4.JavaScript数据类型 JavaScript和Java有什么关系 知识点一 JavaScript基本使用 JS写在哪 还有一种写在中间的,也就是<head>里面 JS一些注意事项 JS修改元素内容 #JS获取对象<…...
Day 32 动态规划part01
今天正式开始动态规划! 理论基础 无论大家之前对动态规划学到什么程度,一定要先看 我讲的 动态规划理论基础。 如果没做过动态规划的题目,看我讲的理论基础,会有感觉 是不是简单题想复杂了? 其实并没有,我讲的理论基础内容,在动规章节所有题目都有运用,所以很重要!…...

winform跨线程更新界面
前言: 大家好,我是上位机马工,硕士毕业4年年入40万,目前在一家自动化公司担任软件经理,从事C#上位机软件开发8年以上!我们在开发C#程序的时候,有时候需要在非Ui主线程更新界面,为了…...

【合作原创】使用Termux搭建可以使用的生产力环境(二)
前言 上期文章没看的可以先从上期文章开始看起 【合作原创】使用Termux搭建可以使用的生产力环境(一)-CSDN博客 目前我们已经完成了FinalShell ssh连接手机Termux的功能了,这期我们继续朝我们的目标前进。今天早上有读者进群以为生成环境指…...

微积分复习笔记 Calculus Volume 2 - 3.3 Trigonometric Substitution
3.3 Trigonometric Substitution - Calculus Volume 2 | OpenStax...

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型
摘要 拍照搜题系统采用“三层管道(多模态 OCR → 语义检索 → 答案渲染)、两级检索(倒排 BM25 向量 HNSW)并以大语言模型兜底”的整体框架: 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后,分别用…...

均衡后的SNRSINR
本文主要摘自参考文献中的前两篇,相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程,其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt 根发送天线, n r n_r nr 根接收天线的 MIMO 系…...
安卓基础(aar)
重新设置java21的环境,临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的: MyApp/ ├── app/ …...

AI+无人机如何守护濒危物种?YOLOv8实现95%精准识别
【导读】 野生动物监测在理解和保护生态系统中发挥着至关重要的作用。然而,传统的野生动物观察方法往往耗时耗力、成本高昂且范围有限。无人机的出现为野生动物监测提供了有前景的替代方案,能够实现大范围覆盖并远程采集数据。尽管具备这些优势…...
JS手写代码篇----使用Promise封装AJAX请求
15、使用Promise封装AJAX请求 promise就有reject和resolve了,就不必写成功和失败的回调函数了 const BASEURL ./手写ajax/test.jsonfunction promiseAjax() {return new Promise((resolve, reject) > {const xhr new XMLHttpRequest();xhr.open("get&quo…...

Rust 开发环境搭建
环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行: rustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu 2、Hello World fn main() { println…...
规则与人性的天平——由高考迟到事件引发的思考
当那位身着校服的考生在考场关闭1分钟后狂奔而至,他涨红的脸上写满绝望。铁门内秒针划过的弧度,成为改变人生的残酷抛物线。家长声嘶力竭的哀求与考务人员机械的"这是规定",构成当代中国教育最尖锐的隐喻。 一、刚性规则的必要性 …...

2.3 物理层设备
在这个视频中,我们要学习工作在物理层的两种网络设备,分别是中继器和集线器。首先来看中继器。在计算机网络中两个节点之间,需要通过物理传输媒体或者说物理传输介质进行连接。像同轴电缆、双绞线就是典型的传输介质,假设A节点要给…...

内窥镜检查中基于提示的息肉分割|文献速递-深度学习医疗AI最新文献
Title 题目 Prompt-based polyp segmentation during endoscopy 内窥镜检查中基于提示的息肉分割 01 文献速递介绍 以下是对这段英文内容的中文翻译: ### 胃肠道癌症的发病率呈上升趋势,且有年轻化倾向(Bray等人,2018&#x…...

【Java多线程从青铜到王者】单例设计模式(八)
wait和sleep的区别 我们的wait也是提供了一个还有超时时间的版本,sleep也是可以指定时间的,也就是说时间一到就会解除阻塞,继续执行 wait和sleep都能被提前唤醒(虽然时间还没有到也可以提前唤醒),wait能被notify提前唤醒…...