思路:通过react-activation实现页面缓存,通过umi-plugin-keep-alive将react-activation注入umi框架,封装页签组件最后通过路由的wrappers属性引入页面。
浏览本博客之前先看一下我的博客实现的功能是否满足需求,实现功能:
- 页面缓存
- 关闭当前页
- 鼠标右键>关闭当前
- 鼠标右键>关闭其他
- 鼠标右键>关闭左侧
- 鼠标右键>关闭右侧
- 鼠标右键>全部关闭(默认跳转到首页)
- 鼠标右键>重新加载(刷新缓存页面)
1.下载依赖
pnpm install react-activation@0.12.4
pnpm install umi-plugin-keep-alive@0.0.1-beta.35
2.修改.umirc.ts文件配置
import { defineConfig } from '@umijs/max';
export default defineConfig({
plugins: ['umi-plugin-keep-alive'],
...
});
3.封装组件
src目录下创建layouts文件夹,创建BaseLayout.tsx文件和BaseTabs.tsx文件
// BaseLayout.tsx
import { KeepAlive, Outlet, useRouteProps } from '@umijs/max';
import React from 'react';
import BaseTabs from './BaseTabs';
export default (): React.ReactElement => {
const { originPath, name } = useRouteProps();
return (
<>
<BaseTabs />
<KeepAlive id={originPath} name={originPath} tabName={name}>
<Outlet />
</KeepAlive>
</>
);
};
// BaseTabs.tsx
import { history, useAliveController, useLocation } from '@umijs/max';
import { Dropdown, Tabs } from 'antd';
import React from 'react';
export default (): React.ReactElement => {
const { pathname } = useLocation();
// 获取缓存列表
const { getCachingNodes, dropScope, clear, refreshScope } =
useAliveController();
const cachingNodes = getCachingNodes();
/**
* 点击tab,跳转页面
*/
const clickTab = (path: string) => {
history.push(path);
};
/**
* 关闭tab,销毁缓存
*/
const editTab = (path: any) => {
dropScope(path);
// 关闭当前页面,需跳转到其他页签
if (path === pathname) {
const index = cachingNodes.findIndex((item) => item.name === path);
if (index > 0) {
history.push(cachingNodes[index - 1].name as string);
} else {
history.push(cachingNodes[1].name as string);
}
}
};
// 关闭当前页
const onCurrent = (e: any) => {
e.domEvent.stopPropagation();
let targetKey = JSON.parse(e?.key).name;
dropScope(targetKey);
// 关闭当前页面,需跳转到其他页签
if (targetKey === pathname) {
const index = cachingNodes.findIndex((item) => item.name === targetKey);
if (index > 0) {
history.push(cachingNodes[index - 1].name as string);
} else {
history.push(cachingNodes[1].name as string);
}
}
};
// 关闭其他
const onOther = (e: any) => {
e.domEvent.stopPropagation();
let targetKey = JSON.parse(e?.key).name;
history.push(targetKey);
clear();
};
//关闭左侧
const onLeft = (e: any) => {
e.domEvent.stopPropagation();
let targetKey = JSON.parse(e?.key).name;
const lastIndex = cachingNodes.findIndex((item) => item.name === pathname);
const currIndex = cachingNodes.findIndex((item) => item.name === targetKey);
if (currIndex > lastIndex) history.push(targetKey);
cachingNodes.forEach((item, index) => {
if (index < currIndex) {
dropScope(item?.name || '');
}
});
};
// 关闭右侧
const onRight = (e: any) => {
e.domEvent.stopPropagation();
let targetKey = JSON.parse(e?.key).name;
const lastIndex = cachingNodes.findIndex((item) => item.name === pathname);
const currIndex = cachingNodes.findIndex((item) => item.name === targetKey);
if (currIndex < lastIndex) history.push(targetKey);
cachingNodes.forEach((item, index) => {
if (index > currIndex) {
dropScope(item?.name || '');
}
});
};
// 关闭全部
const onAll = (e: any) => {
e.domEvent.stopPropagation();
history.push('/home');
clear();
};
// 重新加载
const onRefresh = (e: any) => {
e.domEvent.stopPropagation();
let targetKey = JSON.parse(e?.key).name;
refreshScope(targetKey);
};
const labelDropdown = (name: string, label: string) => {
const lastIndex = cachingNodes.findIndex((item) => item.name === name);
return (
<Dropdown
menu={{
items: [
{
label: '关闭当前',
key: JSON.stringify({ name, key: 'current' }),
disabled: cachingNodes.length <= 1,
onClick: onCurrent,
},
{
label: '关闭其他',
key: JSON.stringify({ name, key: 'other' }),
disabled: cachingNodes.length <= 1,
onClick: onOther,
},
{
label: '关闭左侧',
key: JSON.stringify({ name, key: 'left' }),
disabled: lastIndex === 0,
onClick: onLeft,
},
{
label: '关闭右侧',
key: JSON.stringify({ name, key: 'right' }),
disabled: lastIndex === cachingNodes.length - 1,
onClick: onRight,
},
{
label: '全部关闭',
key: JSON.stringify({ name, key: 'all' }),
onClick: onAll,
disabled: cachingNodes.length <= 1,
},
{
label: '重新加载',
key: JSON.stringify({ name, key: 'refresh' }),
onClick: onRefresh,
},
],
}}
trigger={['contextMenu']}
>
<span>{label}</span>
</Dropdown>
);
};
const tabItems = cachingNodes.map((item: any) => ({
label: labelDropdown(item.name, item.tabName),
key: item.name,
closable: cachingNodes.length > 1,
}));
return (
<Tabs
type="editable-card"
hideAdd
size="small"
activeKey={pathname}
onTabClick={clickTab}
onEdit={editTab}
items={tabItems}
/>
);
};
4.修改路由
routes: [
{
name: '首页',
path: '/home',
component: './Home',
},
{
name: '示例',
path: '/example',
routes: [
{
name: '权限演示',
path: '/example/access',
component: './Access',
wrappers: ['@/layouts/BaseLayout'],
},
{
name: ' CRUD 示例',
path: '/example/table',
component: './Table',
wrappers: ['@/layouts/BaseLayout'],
},
],
},
],