react-router-dom v6版本实现Tabs路由缓存切换
目录
文章目录
概要
效果
完整代码
概要
摆了半年摊,好久没写代码了,今天有人问我怎么实现React-Router-dom类似标签页缓存。后面看了一下router的官网。很久以前用的是react-router v5那个比较容易实现。v6变化挺大,但了解react的机制和react-router的机制就容易了.
想做到切换标签保留页面的内容不变,就要了解react的机制
首先虚拟DOM的机制就是对比,如果找不到就会重新挂载,找到了就更新。
React-Router的机制就是匹配路径,找到了就返回对应的路由组件,找不到返回为null
思路就是v6版本提供了Outlet这个输出子路元素的组件
一般我们会这样写:

但如果要实现标签的话,就不能这样写。但是切换路由,地址一变它就替换了原来的了。所以要做的就是保留所有打开的标签的子路由元素:
主要目的就是保留所有的元素,隐藏路由就行,这样react diff时,还是会找到对应key的路由,这样它只是会更新路由的组件,而不会重新挂载。
如这样写:


效果

完整代码
新建一个html复制进去就可以运行了
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>React-router-dom tabs</title><script src="https://cdn.jsdelivr.net/npm/react@18.2.0/umd/react.production.min.js"></script><script src="https://cdn.jsdelivr.net/npm/react-dom@18.2.0/umd/react-dom.production.min.js"></script><script src="https://cdn.jsdelivr.net/npm/@babel/standalone@7.23.2/babel.min.js"></script><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/antd@5.10.1/dist/reset.min.css"><script src="https://cdn.jsdelivr.net/npm/dayjs@1.11.10/dayjs.min.js"></script><script src="https://cdn.jsdelivr.net/npm/antd@5.10.1/dist/antd.min.js"></script><script src="https://cdn.jsdelivr.net/npm/@ant-design/pro-components@2.6.30/dist/pro-components.min.js"></script><script src="https://cdn.jsdelivr.net/npm/@remix-run/router@1.10.0/dist/router.umd.min.js"></script><script src="https://cdn.jsdelivr.net/npm/react-router@6.17.0/dist/umd/react-router.production.min.js"></script><scriptsrc="https://cdn.jsdelivr.net/npm/react-router-dom@6.17.0/dist/umd/react-router-dom.production.min.js"></script>
</head>
<body><div id="app"></div><script type="text/babel" data-preset="env,react">const { useCallback, useMemo, useEffect, useRef, useState } = Reactconst { ProLayout } = ProComponentsconst { createHashRouter, useOutlet, Navigate, RouterProvider, useLocation, Link, useNavigate } = ReactRouterDOMconst { Tabs, Button, Space, Row, Col, Input, } = antdconst Home = () => {return <div>Home <Input></Input></div>}const DemoA = () => {return <div>DemoA <Input></Input></div>}const DemoB = () => {return <div>DemoB <Input></Input></div>}const ViewPage = (props) => {const outlet = useOutlet()return props.render(outlet)}const BasicLayout = () => {const nav = useNavigate()const location = useLocation()const route = routes[0]const [menuDataMap] = useState(() => new Map())const cacheOutletElements = useRef({}).currentconst [tabActiveKey, setTabActiveKey] = useState('')const [tabItems, setTabItems] = React.useState([])const addTab = useCallback((item) => {const existItem = tabItems.find(it => it.key === item.key)if (!existItem) {setTabItems([...tabItems, {key: item.key,path: item.path,label: item.name}])}setTabActiveKey(item.key)}, [tabItems])const handleSelectMenu = useCallback((selectedKeys) => {console.log('handleSelectMenu', selectedKeys)let menuKey = selectedKeys[selectedKeys.length - 1]let item = menuDataMap.get(menuKey)if (item && item.path) {addTab(item)}}, [addTab])const handleTabChange = useCallback((activeKey) => {setTabActiveKey(activeKey)const item = tabItems.find(d => d.key === activeKey)nav(item.path)}, [tabItems, nav])const handleTabEditChange = useCallback((activeKey, action) => {if (action === 'remove') {delete cacheOutletElements[activeKey]const newItems = tabItems.filter(d => d.key !== activeKey)setTabItems(newItems)if (newItems.length) {handleTabChange(newItems[0].key)}}}, [tabItems, handleTabChange])const renderView = useCallback((routeElement) => {if (!cacheOutletElements[tabActiveKey]) {cacheOutletElements[tabActiveKey] = <div>{routeElement}</div>}return Object.keys(cacheOutletElements).map(key => {const element = cacheOutletElements[key]if (key === tabActiveKey) {return React.cloneElement(element, {key: key,style: {display: 'block'}})} else {return React.cloneElement(element, {key: key,style: {display: 'none'}})}})}, [cacheOutletElements, tabActiveKey])return <ProLayout route={route} onSelect={handleSelectMenu} location={location} menuItemRender={(item, defaultDom, menuProps) => {if (item.children) {return defaultDom}menuDataMap.set(item.path, item)return <Link to={item.path}>{defaultDom}</Link>}}><Tabs hideAdd type='editable-card' onEdit={handleTabEditChange} activeKey={tabActiveKey} onChange={handleTabChange} items={tabItems}></Tabs><ViewPage render={renderView}></ViewPage></ProLayout>}const routes = [{path: '/',element: <BasicLayout></BasicLayout>,children: [{index: true,element: <Navigate to="/home"></Navigate>}, {path: 'home',name: "Home",element: <Home></Home>}, {path: 'a',name: "DemoA",element: <DemoA></DemoA>}, {path: 'b',name: "DemoB",element: <DemoB></DemoB>}]}]const router = createHashRouter(routes, {})const App = () => {return <RouterProvider router={router}></RouterProvider>}ReactDOM.createRoot(document.getElementById('app')).render(<App></App>)</script>
</body>
</html>
相关文章:
react-router-dom v6版本实现Tabs路由缓存切换
目录 文章目录 概要 效果 完整代码 概要 摆了半年摊,好久没写代码了,今天有人问我怎么实现React-Router-dom类似标签页缓存。后面看了一下router的官网。很久以前用的是react-router v5那个比较容易实现。v6变化挺大,但了解react的机制和rea…...
ArcGIS笔记9_数据源缺少空间参考信息?如何定义坐标系?
本文目录 前言Step 1 确定好要赋予给目标文件的恰当坐标系Step 2 定义坐标系 前言 有时从其他软件转换得到了shp文件,拖到ArcGIS后却出现“未知的空间参考:添加的数据源缺少空间参考信息 不能投影”的提示,如下图所示: 这种情况就…...
Elasticsearch实践:ELK+Kafka+Beats对日志收集平台的实现
可以在短时间内搜索和分析大量数据。 Elasticsearch 不仅仅是一个全文搜索引擎,它还提供了分布式的多用户能力,实时的分析,以及对复杂搜索语句的处理能力,使其在众多场景下,如企业搜索,日志和事件数据分析等…...
离线语音与IoT结合:智能家居发展新增长点
离线语音控制和物联网(IoT)相结合在家居中具有广泛的应用和许多优势。离线语音控制是指在设备在本地进行语音识别和处理,而不需要依赖云服务器进行处理。IoT是指借助网络,通过手机APP、小程序远程控制家居设备。 启英泰伦基于AI语…...
STM32MP135和STM32MP157的区别
本文介绍了STMicroelectronics公司推出的两款多核处理器STM32MP135和STM32MP157之间的区别,包括主频、集成硬件模块数量、内存大小和电压调节模块等方面。 STMicroelectronics是一家领先的半导体解决方案提供商,在嵌入式系统领域有着丰富的经验。他们…...
微信小程序文本横向无缝滚动
背景: 微信小程序中列表宽度不够长,其中某字段显示不完整,因此要使其自动滚动。 (最初看网上很多用定时器实现,但他们的案例中都只是一个横幅、用定时器也无所谓。但是我的需求中是一个上下无限滚动的列表,…...
Layui 主窗口调用 iframe 弹出框模块,获取控件的相应值
var iframeWindow window[layui-layer-iframe index]; iframeWindow.layui.tree............(这里就可以操作tree里面的内容了)。var chrild layero.find(iframe).contents(); chrild.layui.tree (这样是调用不到的)。var child layer.getChildFrame(); child.layui.tree(这…...
镜头边缘的解析力通常比中心差很多的原因是什么?
1、问题背景 之前有总结过一篇文章,“ 相机出图画面一半清晰,一半模糊的原因是什么?”里面有描述到关于镜头边缘的清晰度通常比中心要差的原因主要是光的折射导致的,有读者指出问题,折射率是和传输介质相关࿰…...
“控制情绪,理性交流”刍议
今天,本“人民体验官”还是回避推广人民日报官方微博文化产品《走出低谷期的9个习惯》。 截图:来源“人民体验官”推广平台 之前,由于笔者读过《人民日报》曾经发表过的关于“学会管理情绪 ”的文章,对文章中这些观点深表认同&…...
UI基础之插画分类优漫动游
一、UI插画分类 UI基础之插画分类 1.扁平插画 优点∶快速上手,同时画风简洁明了,突出重 点,能够快速的让用户了解内容 缺点:过于简洁,运用的也比较普遍,视觉上难以让用户记住 2.肌理插…...
Vue 3.0中Treeshaking特性是什么?
一、是什么 Tree shaking 是一种通过清除多余代码方式来优化项目打包体积的技术,专业术语叫 Dead code elimination 简单来讲,就是在保持代码运行结果不变的前提下,去除无用的代码 如果把代码打包比作制作蛋糕,传统的方式是把鸡…...
SQL union all的使用
背景: 公司业务开发需要将两个取出两个相同表结构(原料、辅料)的数据,组成一个新视图,使用了UNION ALL SET QUOTED_IDENTIFIER ON SET ANSI_NULLS ON GOCREATE view vw_rawmaterial_ny_list as ( select id,ccode,cc…...
docker 安装 Centos7
1. 从docker 安装 Centos7 查看有哪些 centos7 系统:docker search centos72. 安装 centos7 docker pull docker.io/ansible/centos7-ansible3.使用镜像创建容器 docker run -itd -p 8022:22 --namevm01 -v /bodata:/bodata -h vm01 --privilegedtrue 688353a31…...
Kubernetes技术与架构-Ingress
Ingress是一个流量网关,其根据配置的URI路径路由规则,为运行在Kubernetes集群中的Service分发流量,从系统架构设计的角度看,Ingress位于Service的上层,本文主要描述Ingress的基本使用方式。 如上所示,clien…...
基于Java的文物管理系统设计与实现(源码+lw+部署文档+讲解等)
文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序(小蔡coding) 代码参考数据库参考源码获取 前言 💗博主介绍:✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者&am…...
uniapp图片加水印
1、uniapp加水印 1.1、创建画布容器 <canvas class"watermark-canvas" id"watermark-canvas" canvas-id"watermark-canvas":style"{ width: canvasWidth, height: canvasHeight }" /> 1.2、获取水印内容 async getLocation(…...
react中JSX基础与useState的基本使用 + 评论显示删除需求案例
参考视频:https://www.bilibili.com/video/BV1ZB4y1Z7o8/?p3&spm_id_frompageDriver&vd_source5c584bd3b474d579d0bbbffdf0437c70 如果没有安装create-react-app需要先全局安装 命令:npm i -g create-react-app1.快速搭建开发环境 create-re…...
【OpenCV实现鼠标绘图,轨迹栏做调色板,图像的基本操作】
文章目录 鼠标绘图轨迹栏做调色板图像的基本操作 鼠标绘图 在OpenCV中操作鼠标事件 函数:cv.setMouseCallback() 目的是在鼠标双击的地方画一个圆。首先,我们需要创建一个鼠标回调函数,该函数会在鼠标事件发生时执行。鼠标事件包括左键按下…...
2023年中国自动排气阀产业链、市场规模及存在问题分析]图[
自动排气阀是一种用于排除管道、容器或设备中累积的空气或气体的装置。在液体流动系统中,气体或空气可能会积聚在管道或容器中,影响流体流动、导致气锁和能效降低。自动排气阀的作用是在系统中的气体达到一定压力时,自动地释放气体࿰…...
服务器往浏览器推消息(SSE)应用
1,SSE 和 WebSocket 对比 SSE(服务器发送事件) SSE是一种基于HTTP的单向通信机制,用于服务器向客户端推送数据。它的工作原理如下: 建立连接:客户端通过发送HTTP请求与服务器建立连接。在请求中ÿ…...
[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?
🧠 智能合约中的数据是如何在区块链中保持一致的? 为什么所有区块链节点都能得出相同结果?合约调用这么复杂,状态真能保持一致吗?本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里…...
汽车生产虚拟实训中的技能提升与生产优化
在制造业蓬勃发展的大背景下,虚拟教学实训宛如一颗璀璨的新星,正发挥着不可或缺且日益凸显的关键作用,源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例,汽车生产线上各类…...
Rust 异步编程
Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...
WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)
一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解,适合用作学习或写简历项目背景说明。 🧠 一、概念简介:Solidity 合约开发 Solidity 是一种专门为 以太坊(Ethereum)平台编写智能合约的高级编…...
网站指纹识别
网站指纹识别 网站的最基本组成:服务器(操作系统)、中间件(web容器)、脚本语言、数据厍 为什么要了解这些?举个例子:发现了一个文件读取漏洞,我们需要读/etc/passwd,如…...
【JVM】Java虚拟机(二)——垃圾回收
目录 一、如何判断对象可以回收 (一)引用计数法 (二)可达性分析算法 二、垃圾回收算法 (一)标记清除 (二)标记整理 (三)复制 (四ÿ…...
免费数学几何作图web平台
光锐软件免费数学工具,maths,数学制图,数学作图,几何作图,几何,AR开发,AR教育,增强现实,软件公司,XR,MR,VR,虚拟仿真,虚拟现实,混合现实,教育科技产品,职业模拟培训,高保真VR场景,结构互动课件,元宇宙http://xaglare.c…...
从 GreenPlum 到镜舟数据库:杭银消费金融湖仓一体转型实践
作者:吴岐诗,杭银消费金融大数据应用开发工程师 本文整理自杭银消费金融大数据应用开发工程师在StarRocks Summit Asia 2024的分享 引言:融合数据湖与数仓的创新之路 在数字金融时代,数据已成为金融机构的核心竞争力。杭银消费金…...
嵌入式常见 CPU 架构
架构类型架构厂商芯片厂商典型芯片特点与应用场景PICRISC (8/16 位)MicrochipMicrochipPIC16F877A、PIC18F4550简化指令集,单周期执行;低功耗、CIP 独立外设;用于家电、小电机控制、安防面板等嵌入式场景8051CISC (8 位)Intel(原始…...
十九、【用户管理与权限 - 篇一】后端基础:用户列表与角色模型的初步构建
【用户管理与权限 - 篇一】后端基础:用户列表与角色模型的初步构建 前言准备工作第一部分:回顾 Django 内置的 `User` 模型第二部分:设计并创建 `Role` 和 `UserProfile` 模型第三部分:创建 Serializers第四部分:创建 ViewSets第五部分:注册 API 路由第六部分:后端初步测…...
