思路:通过react-activation实现页面缓存,通过umi-plugin-keep-alive将react-activation注入umi框架,封装页签组件最后通过路由的wrappers属性引入页面。
浏览本博客之前先看一下我的博客实现的功能是否满足需求,实现功能:
- 页面缓存
- 关闭当前页
- 阻止事件传播
- 鼠标右键>关闭当前
- 鼠标右键>关闭其他
- 鼠标右键>关闭左侧
- 鼠标右键>关闭右侧
- 鼠标右键>全部关闭(默认跳转到首页)
- 鼠标右键>重新加载(刷新缓存页面)
pnpm install [email protected]
pnpm install [email protected]
import { defineConfig } from '@umijs/max';
export default defineConfig({
plugins: ['umi-plugin-keep-alive'],
...
});
src目录下创建layouts文件夹,创建BaseLayout.tsx文件和BaseTabs.tsx、index.less文件
// 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/index.tsx
import { history, useAliveController, useLocation } from '@umijs/max';
import { Dropdown, Tabs } from 'antd';
import React, { useState } from 'react';
import './index.less';
export default (): React.ReactElement => {
const { pathname } = useLocation();
// 获取缓存列表
const { getCachingNodes, dropScope, clear, refreshScope } =
useAliveController();
const cachingNodes = getCachingNodes();
const [open, setOpen] = useState<{ path: string; open: boolean }>({
path: '',
open: false,
});
// 阻止右键事件冒泡
const onRightClick = (
e: React.MouseEvent,
name: string,
) => open.open && open.path === name && e.stopPropagation();
// 点击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) => {
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) => {
let targetKey = JSON.parse(e?.key).name;
history.push(targetKey);
clear();
};
//关闭左侧
const onLeft = (e: any) => {
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) => {
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 = () => {
history.push('/home');
clear();
};
// 重新加载
const onRefresh = (e: any) => {
let targetKey = JSON.parse(e?.key).name;
refreshScope(targetKey);
};
const labelDropdown = (name: string, label: string) => {
const lastIndex = cachingNodes.findIndex((item) => item.name === name);
return (
onRightClick(e, name)}>
setOpen({ path: name, open: e })}
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,
},
],
}}
>
1 ? 'dropdown-label' : ''}>
{label}
);
};
const tabItems = cachingNodes.map((item: any) => ({
label: labelDropdown(item.name, item.tabName),
key: item.name,
closable: cachingNodes.length > 1,
}));
return (
);
};
// index.less
.base-tabs {
.ant-dropdown-trigger {
padding: 5px 10px;
height: 100%;
}
.dropdown-label {
padding: 5px 6px 5px 10px;
height: 100%;
}
.ant-tabs-tab {
padding: 0 !important;
}
.ant-tabs-tab-remove {
margin-left: 0 !important;
margin-right: 2px !important;
padding-left: 0px !important;
}
}
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'],
},
],
},
],