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请求与服务器建立连接。在请求中ÿ…...
linux之kylin系统nginx的安装
一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源(HTML/CSS/图片等),响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址,提高安全性 3.负载均衡服务器 支持多种策略分发流量…...
基于服务器使用 apt 安装、配置 Nginx
🧾 一、查看可安装的 Nginx 版本 首先,你可以运行以下命令查看可用版本: apt-cache madison nginx-core输出示例: nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...
【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表
1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...
Spring Boot面试题精选汇总
🤟致敬读者 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉 📘博主相关 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...
Unit 1 深度强化学习简介
Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库,例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体,比如 SnowballFight、Huggy the Do…...
CMake控制VS2022项目文件分组
我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...
LeetCode - 199. 二叉树的右视图
题目 199. 二叉树的右视图 - 力扣(LeetCode) 思路 右视图是指从树的右侧看,对于每一层,只能看到该层最右边的节点。实现思路是: 使用深度优先搜索(DFS)按照"根-右-左"的顺序遍历树记录每个节点的深度对于…...
推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材)
推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材) 这个项目能干嘛? 使用 gemini 2.0 的 api 和 google 其他的 api 来做衍生处理 简化和优化了文生图和图生图的行为(我的最主要) 并且有一些目标检测和切割(我用不到) 视频和 imagefx 因为没 a…...
redis和redission的区别
Redis 和 Redisson 是两个密切相关但又本质不同的技术,它们扮演着完全不同的角色: Redis: 内存数据库/数据结构存储 本质: 它是一个开源的、高性能的、基于内存的 键值存储数据库。它也可以将数据持久化到磁盘。 核心功能: 提供丰…...
区块链技术概述
区块链技术是一种去中心化、分布式账本技术,通过密码学、共识机制和智能合约等核心组件,实现数据不可篡改、透明可追溯的系统。 一、核心技术 1. 去中心化 特点:数据存储在网络中的多个节点(计算机),而非…...
