Using tabs navigation in background projects is an important function
Let’s introduce how to implement this function with the umi framework and the corresponding plug-in.
Reference: react-activation
Reference implementation: implements a
The current architecture design used in the project is umi3+react17+antd pro5
1. Introduce and use related plug-ins
Plug-in address: umi-plugin-keep-alive
npm install umi-plugin-keep-alive --save
# or
yarn add umi-plugin-keep-alive
2. Public component packaging
Create KeepAvlieTabs under the components folder
Please modify your own requirements for the corresponding less style
For related keepAlive and life cycle usage plans and questions, please go to the above link react-activation to view
Create index.tsx file
// /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>
);
}
Create Tab.tsx file
// /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>
);
}
Create type file type.ts
export type CachingNode = {
createTime: number;
updateTime: number;
name?: string;
id: string;
[key: string]: any;
};
Create a style less file
.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. Use keepAlive in the component
Create 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 mounted to the interface
Our page layout has left and right structure, in app.tsx, mounted in our head, beyond the display scroll bar, please add specific styles and functions by yourself
The right mouse button menu is not implemented, close all, switch left and right, please add by yourself, antd has related components
export const layout: RunTimeLayoutConfig = ({ initialState }: any) => {
return {
// 控制组件是否可缓存
disableMobile: true,
headerContentRender: () => <KeepAliveTabs />,
...
};
};
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。