antd-design-pro实现多页签,切换页签保留缓存,keep-alive
感谢该大佬提供的组件:GitHub - CJY0208/react-activation: Hack <KeepAlive /> for Reactreact 里 keep-alive 的实现目前是黑科技,会有些问题使用过程中遇到问题的话,可以优先看这儿https://github.com/CJY0208/react-activation/blob/master/README_CN.m
感谢该大佬提供的组件:GitHub - CJY0208/react-activation: Hack <KeepAlive /> for React
react 里 keep-alive 的实现目前是黑科技,会有些问题
使用过程中遇到问题的话,可以优先看这儿
https://github.com/CJY0208/react-activation/blob/master/README_CN.md#breaking-change-%E7%94%B1%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86%E5%BC%95%E5%8F%91%E7%9A%84%E9%A2%9D%E5%A4%96%E9%97%AE%E9%A2%98
- 安装依赖包
yarn add react-activation
- 配置plugin
1、因为我的项目是antd-design-pro,所以不需要按照组件大佬那样配置,只需要安装新的依赖 umi-plugin-keep-alive。antd-design-pro搭建的项目只需要这一步!!!
yarn add umi-plugin-keep-alive
2、若是普通react,则按照大佬那样,在配置文件增加:
{
"plugins": [
"react-activation/babel"
]
}
或者如果这样报错的话,在config.js文件
// umi 的 babel 配置要加在这儿
extraBabelPlugins: ['react-activation/babel'],
- 使用
比如你希望不让 Counter 卸载,那就包在 Counter 外头
- 注意点
比如我使用的时候,加在PageContainer内,我的PageContainer添加了多页签,所以我希望切换页面,页面保留缓存。
{/* KeepAlive必须加上name和id,否则切换menu,页面不会变化 */}
<PageContainer
breadcrumb={'none'}
title={false}
// tabList={tabList}
tabList={newTabListData}
tabProps={{
type: 'editable-card',
hideAdd: true,
onEdit: (e, action) => {
if (action === 'remove') {
remove(e);
}
},
}}
tabActiveKey={activeKey}
// tabActiveKey={getTabKey()}
onTabChange={handleTabChange}
>
{/* KeepAlive必须加上name和id,否则切换menu,页面不会变化 */}
{/* {newTabListData?.length > 0 ? props.children : null} */}
{newTabListData?.length > 0 ? (
<KeepAlive
when={true}
name={props.children?.props?.location?.pathname}
id={props.children?.props?.location?.pathname}
saveScrollPosition="screen"
>
{props.children}
</KeepAlive>
) : null}
</PageContainer>
- antd-design-pro切换菜单时,清除缓存
我实际使用的时候,在移除tabs时,才清除
app.jsx
import { PageContainer, PageLoading } from '@ant-design/pro-layout';
import { useState, useEffect } from 'react';
import { history, useModel } from 'umi';
import RightContent from '@/components/RightContent';
import { currentUser as queryCurrentUser } from './services/ant-design-pro/api';
import { QuestionCircleOutlined } from '@ant-design/icons';
import KeepAlive, { withAliveScope, useAliveController } from 'react-activation';
import { getTab, isEmpty } from '../utils/common';
import styles from './pages/common.less';
import { Modal } from 'antd';
import { Link } from 'react-router-dom';
import customMenuDate from './customMenu';
const isDev = process.env.NODE_ENV === 'development';
const loginPath = '/user/login';
/** 获取用户信息比较慢的时候会展示一个 loading */
export const initialStateConfig = {
loading: <PageLoading />,
};
/**
* @see https://umijs.org/zh-CN/plugins/plugin-initial-state
* */
export async function getInitialState() {
const fetchUserInfo = async () => {
try {
const msg = await queryCurrentUser();
return msg.data;
} catch (error) {
history.push(loginPath);
}
return undefined;
}; // 如果是登录页面,不执行
if (history.location.pathname !== loginPath) {
const currentUser = await fetchUserInfo();
return {
fetchUserInfo,
currentUser,
settings: {},
};
}
return {
fetchUserInfo,
settings: {},
};
} // ProLayout 支持的api https://procomponents.ant.design/components/layout
const LeaveModal = (props) => {
const [leaveModalVisible, changeLeaveModalVisible] = useState(false);
useEffect(() => {
changeLeaveModalVisible(props?.leaveModalVisible);
}, [props?.leaveModalVisible]);
const { newTabList, changeTab } = useModel('tabList', (ret) => ({
changeTab: ret.changeTab,
newTabList: ret.newTabList,
}));
const { changeDetailStatus } = useModel('detailStatus', (ret) => ({
changeDetailStatus: ret.changeDetailStatus,
}));
return (
<Modal
width={640}
destroyOnClose
title={'离开提示'}
visible={leaveModalVisible}
onCancel={() => {
changeLeaveModalVisible(false);
}}
onOk={async () => {
changeLeaveModalVisible(false);
changeDetailStatus(false);
history.push(`${props?.path}`);
let tabListArray = [];
tabListArray = await getTab(props?.path, newTabList);
changeTab(tabListArray);
}}
>
<div className={styles.modalView}>
<QuestionCircleOutlined style={{ color: '#faad14', fontSize: 30, marginRight: 18 }} />
<div>
<p>离开此页面,系统不会保存所做修改,确认离开?</p>
</div>
</div>
</Modal>
);
};
export const layout = ({ initialState }) => {
return {
rightContentRender: () => <RightContent />,
//点击子菜单的时候,其他菜单不自动收起来
// openKeys: false,
disableContentMargin: false,
// waterMarkProps: {
// content: initialState?.currentUser?.name,
// },
onPageChange: async () => {
const { location } = history; // 如果没有登录,重定向到 login
// console.log('============onPageChange location', location);
if (location.pathname === '/') {
history.push('/404');
}
// if (!initialState?.currentUser && location.pathname !== loginPath) {
// history.push(loginPath);
// }
},
menuDataRender: () => customMenuDate,
//自定义menu
menuItemRender: (item, dom) => {
const [pathname, changePathname] = useState('');
const [leaveModalVisible, changeLeaveModalVisible] = useState(false);
const { detailStatus } = useModel('detailStatus', (ret) => ({
detailStatus: ret.detailStatus,
}));
async function changeMenu() {
const { location } = history;
if (location.pathname.includes('/detail') && detailStatus) {
changePathname(item.path);
changeLeaveModalVisible(true);
} else {
history.push(item.path);
}
}
return (
<>
<LeaveModal path={pathname} leaveModalVisible={leaveModalVisible} />
<div onClick={() => changeMenu()}>{dom}</div>
</>
);
},
//自定义有子menu
// subMenuItemRender: (item, dom) => {
// const [pathname, changePathname] = useState('');
// const [leaveModalVisible, changeLeaveModalVisible] = useState(false);
// const { detailStatus } = useModel('detailStatus', (ret) => ({
// detailStatus: ret.detailStatus,
// }));
// function changeMenu() {
// const { location } = history;
// if (location.pathname.includes('/detail') && detailStatus) {
// changePathname(item.path);
// changeLeaveModalVisible(true);
// } else {
// history.push(item.path);
// }
// }
// return (
// <>
// <LeaveModal path={pathname} leaveModalVisible={leaveModalVisible} />
// <div onClick={() => changeMenu()}>{dom}</div>
// </>
// );
// },
//自定义面包屑
itemRender: (route, params, routes, paths) => {
const [leaveModalVisible, changeLeaveModalVisible] = useState(false);
const { detailStatus } = useModel('detailStatus', (ret) => ({
detailStatus: ret.detailStatus,
}));
function changeBread() {
const { location } = history;
if (location.pathname.includes('/detail') && detailStatus) {
changeLeaveModalVisible(true);
} else {
history.push(route.path);
}
}
return (
<>
<LeaveModal path={route.path} leaveModalVisible={leaveModalVisible} />
<span to={route.path} onClick={() => changeBread()} style={{ cursor: 'pointer' }}>
{route.breadcrumbName}
</span>
</>
);
},
menuHeaderRender: undefined,
// 自定义 403 页面
// unAccessible: <div>unAccessible</div>,
// 增加一个 loading 的状态
// childrenRender: (children) => {
// if (initialState.loading) return <PageLoading />;
// return children;
// },
childrenRender: (children) => {
const { newTabList, changeTab } = useModel('tabList', (ret) => ({
changeTab: ret.changeTab,
newTabList: ret.newTabList,
}));
const { detailStatus, changeDetailStatus } = useModel('detailStatus', (ret) => ({
detailStatus: ret.detailStatus,
changeDetailStatus: ret.changeDetailStatus,
}));
const [activeKey, changeActiveKey] = useState('');
const [newTabListData, changeNewTabList] = useState([]);
const [leaveModalVisible, changeLeaveModalVisible] = useState(false);
const [newActiveKey, changeNewActiveKey] = useState('');
const [handleStatus, changeHandleStatus] = useState('');
const { drop, dropScope, clear, getCachingNodes } = useAliveController();
const { location } = history;
useEffect(async () => {
let tabListArray = [];
if (location?.pathname.includes('/detail')) {
changeActiveKey(location?.pathname.split('/detail')[0]);
let detail = await findCompletePath(location?.pathname.split('/detail')[0]);
tabListArray = await getTab(detail, newTabList);
} else {
let detail = await findCompletePath(location?.pathname);
if (isEmpty(detail)) {
tabListArray = await getTab({ key: '/404', tab: '欢迎', closable: false }, newTabList);
changeActiveKey('/404');
} else {
tabListArray = await getTab(detail, newTabList);
changeActiveKey(location?.pathname);
}
}
changeNewTabList(tabListArray);
changeTab(tabListArray);
}, [location?.pathname]);
function findCompletePath(passName) {
for (let i = 0; i < customMenuDate?.length; i++) {
for (let j = 0; j < customMenuDate[i].routes?.length; j++) {
let detail = customMenuDate[i].routes[j];
if (detail.path === passName) {
return { tab: detail.name, key: detail.path };
} else {
for (let k = 0; k < detail.routes?.length; k++) {
if (detail.routes[k].path === passName) {
return { tab: detail.routes[k].name, key: detail.routes[k].path };
}
}
}
}
}
}
const handleTabChange = (key) => {
if (location?.pathname.includes('/detail') && detailStatus) {
changeLeaveModalVisible(true);
changeNewActiveKey(key);
changeHandleStatus('onChange');
} else {
changeActiveKey(key);
}
};
const remove = (key) => {
if (location?.pathname.includes('/detail') && detailStatus) {
changeLeaveModalVisible(true);
changeNewActiveKey(key);
changeHandleStatus('remove');
} else {
removeEvent(key);
}
};
const removeEvent = (key) => {
dropScope(key);
drop(key);
let lastIndex;
let activeKeyRemove = _.cloneDeep(activeKey);
newTabList.forEach((pane, i) => {
if (pane.key === key) {
lastIndex = i - 1;
}
});
const newTabListFilter = newTabList.filter((pane) => pane.key !== key);
if (newTabListFilter.length && activeKeyRemove === key) {
if (lastIndex >= 0) {
activeKeyRemove = newTabListFilter[lastIndex].key;
} else {
activeKeyRemove = newTabListFilter[0].key;
}
}
changeTab(newTabListFilter);
changeNewTabList(newTabListFilter);
changeActiveKey(activeKeyRemove);
};
useEffect(() => {
if (activeKey) {
history.push(`${activeKey}`);
}
}, [activeKey]);
return (
<div className={styles.tabsContainer}>
{location?.pathname.includes('/detail') ? (
<PageContainer
title={false}
tabList={newTabListData}
tabProps={{
type: 'editable-card',
hideAdd: true,
onEdit: (e, action) => {
if (action === 'remove') {
remove(e);
}
},
}}
tabActiveKey={activeKey}
onTabChange={handleTabChange}
>
{children}
{/* {newTabListData?.length > 0 ? <Detail /> : null} */}
</PageContainer>
) : (
<PageContainer
breadcrumb={'none'}
title={false}
tabList={newTabListData}
tabProps={{
type: 'editable-card',
hideAdd: true,
onEdit: (e, action) => {
if (action === 'remove') {
remove(e);
}
},
}}
tabActiveKey={activeKey}
onTabChange={handleTabChange}
>
{/* KeepAlive必须加上name和id,否则切换menu,页面不会变化 */}
{/* {newTabListData?.length > 0 ? props.children : null} */}
{newTabList?.length > 0 ? (
<KeepAlive
when={true}
name={location?.pathname}
id={location?.pathname}
saveScrollPosition="screen"
>
{children}
</KeepAlive>
) : null}
</PageContainer>
)}
<Modal
width={640}
destroyOnClose
maskClosable={false}
title={'离开提示'}
visible={leaveModalVisible}
onCancel={() => {
changeLeaveModalVisible(false);
}}
onOk={() => {
changeLeaveModalVisible(false);
changeDetailStatus(false);
if (handleStatus === 'onChange') {
changeActiveKey(newActiveKey);
} else if (handleStatus === 'remove') {
removeEvent(newActiveKey);
}
}}
>
<div className={styles.modalView}>
<QuestionCircleOutlined style={{ color: '#faad14', fontSize: 30, marginRight: 18 }} />
<div>
<p>离开此页面,系统不会保存所做修改,确认离开?</p>
</div>
</div>
</Modal>
</div>
);
},
...initialState?.settings,
};
};
更多推荐
所有评论(0)