感谢该大佬提供的组件: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,页面不会变化 */}
        <PageContainerbreadcrumb={'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 ? (<KeepAlivewhen={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/layoutconst 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 (<Modalwidth={640}destroyOnClosetitle={'离开提示'}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,//自定义menumenuItemRender: (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') ? (<PageContainertitle={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>) : (<PageContainerbreadcrumb={'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 ? (<KeepAlivewhen={true}name={location?.pathname}id={location?.pathname}saveScrollPosition="screen">{children}</KeepAlive>) : null}</PageContainer>)}<Modalwidth={640}destroyOnClosemaskClosable={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,};
};
 


















