react中使用keepAlive实现导航tabs
Posted 万年打野易大师
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了react中使用keepAlive实现导航tabs相关的知识,希望对你有一定的参考价值。
在后台项目中使用tabs导航是一个重要的功能
下面介绍一下如果配合umi框架和对应的插件实现此功能
参考:react-activation
参考实现:实现一个tabs
目前项目用的架构设计是umi3+react17+antd pro5
1.引入使用相关插件
插件地址: umi-plugin-keep-alive
npm install umi-plugin-keep-alive --save
# or
yarn add umi-plugin-keep-alive
2.公共组件封装
components文件夹下创建KeepAvlieTabs
对应的less样式请自行修改自己的需求
相关的keepAlive与生命周期使用方案及问题请到上述链接react-activation查看
创建index.tsx文件
// /components/KeepAvlieTabs/index.tsx
import { useAliveController } from \'react-activation\';
import { arrayDeduplicate } from \'@/utils/Array\';
import type { CachingNode } from \'./type\';
import Tab from \'./Tab\';
import styles from \'./index.less\';
import { useHistory, useLocation } from \'umi\';
export default function KeepAliveTabs() {
// history导航
const history = useHistory();
// 本地路由信息
const location = useLocation();
// 获取缓存节点方法和信息
const { getCachingNodes } = useAliveController();
const cachingNodes: CachingNode[] = getCachingNodes();
// 因为是异步组件,需要在这儿处理一下缓存中的重复问题
let nodes: CachingNode[] = arrayDeduplicate(cachingNodes, \'path\');
// 首页不参与tabs切换的关闭操作
nodes = nodes.filter((item) => item.path !== \'/home\');
return (
<ul className={styles[\'alive-tabs\']}>
<li
className={location.pathname === \'/home\' ? styles.home_active : styles.home_deactive}
onClick={() => {
history.push(\'/home\');
}}
>
<div className="tags-nav">
<span>首页</span>
</div>
</li>
{nodes.map((node) => (
<Tab key={node!.id} node={node} />
))}
</ul>
);
}
创建Tab.tsx文件
// /components/KeepAvlieTabs/Tab.tsx
import { useHistory, useLocation } from \'umi\';
import { useAliveController } from \'react-activation\';
import { CloseCircleOutlined } from \'@ant-design/icons\';
import type { CachingNode } from \'./type\';
import styles from \'./index.less\';
export default function Tab({ node }: { node: CachingNode }) {
const history = useHistory();
const location = useLocation();
// 同上,dropScope是释放节点,点删除后删掉当前节点信息
const { getCachingNodes, dropScope } = useAliveController();
const cachingNodes: CachingNode[] | any[] = getCachingNodes();
// 执行tab的删除操作
function dropTab(e: React.MouseEvent<htmlButtonElement>) {
e.stopPropagation();
// 如果关闭激活中的 KeepAlive Tab,需要先离开当前路由
// 触发 KeepAlive unactivated 后再进行 drop
if (location.pathname === node.path) {
// 路由异步加载控制
const unlisten = history.listen(() => {
setTimeout(() => {
dropScope(node.name as string | RegExp);
}, 30);
});
unlisten();
// 前往排除当前 node 后的最后一个 tab
if (cachingNodes.length <= 1) {
history.push(\'/\');
} else {
const { path } = cachingNodes.filter((item) => item.path !== node.path).pop();
history.push(path);
}
} else {
dropScope(node.name as string | RegExp);
}
}
// 设置当前tab的样式
const className = () => {
if (location.pathname === node.path) {
if (location.pathname === \'/home\') {
return `${styles.active} ${styles.home_active}`;
}
return `${styles.active}`;
}
return `${styles.deactive}`;
};
return (
<li
className={className()}
onClick={() => {
history.push(node.path);
}}
>
<div className="tags-nav">
<span>{node.name}</span>
{<CloseCircleOutlined className={styles[\'close-btn\']} onClick={dropTab} />}
</div>
</li>
);
}
创建类型文件type.ts
export type CachingNode = {
createTime: number;
updateTime: number;
name?: string;
id: string;
[key: string]: any;
};
创建样式less文件
.alive-tabs {
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
white-space: nowrap;
background: #fff;
li {
position: relative;
display: inline-block;
padding: 0 28px 0 10px;
line-height: 32px;
vertical-align: top;
list-style: none;
border-right: solid 1px #e6e6e6;
cursor: pointer;
transition: background 0.2s;
}
.active {
height: 32px;
background: rgba(#1890ff, 0.1);
border-bottom: 2px solid #1890ff;
// background-color: #42b983;
}
.home_active {
height: 32px;
padding: 0 10px;
background: rgba(#1890ff, 0.1);
border-bottom: 2px solid #1890ff;
// background-color: #42b983;
}
.home_deactive {
padding: 0 10px;
}
.deactive {
background: #fff;
}
.close-btn {
position: absolute;
top: 11px;
right: 10px;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
line-height: 0;
outline: none;
// transform: translateY(-50%);
}
}
3.在组件中使用keepAlive
创建Books/index.tsx
import React from \'react\';
import { KeepAlive, useActivate, useUnactivate } from \'react-activation\';
const Books: React.FC = () => {
useActivate(()=>{
console.log("我被激活了");
})
useUnactivate(()=>{
console.log("我被缓存了");
})
return <div>我是被缓存的组件</div>;
};
export default (props: any) => {
// 下面使用这样的方式去控制路由操作
// 是为了同步显示路由定义中的title和匹配路由path做对应的操作
// 可根据自行需求修改当前的逻辑
return (
<KeepAlive name={props.route.name} path={props.route.path} saveScrollPosition="screen">
<Books />
</KeepAlive>
);
};
4.keepAliveTabs挂载到界面上
我们的页面布局左上下结构,在app.tsx中,挂载到我们的头部中,超出显示滚动条,具体的样式和功能请自行添加
未实现鼠标右键菜单关闭所有,左右切换,请自行添加,antd有相关的组件
export const layout: RunTimeLayoutConfig = ({ initialState }: any) => {
return {
// 控制组件是否可缓存
disableMobile: true,
headerContentRender: () => <KeepAliveTabs />,
...
};
};
以上是关于react中使用keepAlive实现导航tabs的主要内容,如果未能解决你的问题,请参考以下文章
ReactNative进阶(二十四):react-native-scrollable-tab-view标签导航器组件详解
ReactNative进阶(十五):react-native-tab-navigator实现底部导航栏
底部导航组件组件react-native-tab-navigator的使用
react-native-tab-view 从反应导航屏幕跳转到特定选项卡