一、常见方案
- 安装依赖
yarn add umi-plugin-keep-alive@^0.0.1-beta.35
- umi配置文件增加插件注入
// .umirc.ts
{
plugins: ['umi-plugin-keep-alive'],
}
- 项目入口文件App.tsx增加如下代码,修复keepalive组件则umi中无法与useAccess共存的问题
// App.tsx
import { autoFixContext } from 'react-activation';
import jsxDevRuntime from 'react/jsx-dev-runtime';
import jsxRuntime from 'react/jsx-runtime';
autoFixContext(
[jsxRuntime, 'jsx', 'jsxs', 'jsxDEV'],
[jsxDevRuntime, 'jsx', 'jsxs', 'jsxDEV'],
);
- 定义一个高阶函数:when函数最好由外部传递,除非全局统一的逻辑可以写在这里
import { KeepAlive, history } from 'umi';
const KeepAliveWrap = (WrapperCpn: React.FC, config: any = {}) => {
return (props: any) => {
const { location } = history;
return (
<KeepAlive
{...config}
id={location.pathname}
when={() => {
console.log(history);
/** 如果跳转的页面是带import字符串的则缓存路由 */
if (history.location.pathname.includes('import')) {
return true;
}
// 否则不缓存
return false;
}}
>
<WrapperCpn {...props} />
</KeepAlive>
);
};
};
export default KeepAliveWrap;
- 使用的时候,直接包裹一下需要导出的tsx页面组件即可
const PlanHospitalDetail: React.FC = () => {
return <div></div>
}
export default KeepAliveWrap(PlanHospitalDetail);
二、hack方案:基于路由监听,将数据保存到window,页面渲染的时候再取出来重新赋值
// 实现一个useRouterCache.tsx的hook
import { history, useLocation } from '@umijs/max';
import { useEffect } from 'react';
type typePageStore = {
[key: string]: any;
};
type typeOnFn = (location: Location, action: string) => void;
type typeListenerFn = {
getData: () => typePageStore;
setData: (data: Record<string, any>) => void;
pageRouteStore: typePageStore;
location: Location;
action: string;
};
type typeRouterCacheoptions = {
pathObj: {
onPop: (params: typeListenerFn) => void;
onPush: (params: typeListenerFn) => void;
};
/** 路由白名单 */
pathIncludes: string[];
};
export const useRouterCache = (options: typeRouterCacheoptions) => {
const location = useLocation();
const pathName = location.pathname;
const { pathObj, pathIncludes } = options;
const initFn = (pathObj) => {
let popFnList: typeOnFn[] = [];
let pushFnList: typeOnFn[] = [];
let { onPop, onPush } = pathObj;
let pageRouteStore = window.pageRouteStore;
let getData = () => {
return window.pageRouteStore?.[pathName];
};
let setData = (data = {}) => {
let originObj = window.pageRouteStore[pathName];
window.pageRouteStore[pathName] = {
...originObj,
...data,
};
};
pushFnList.push((location, action) => {
onPush({
getData,
setData,
pageRouteStore,
location,
action,
});
});
popFnList.push((location, action) => {
onPop({
getData,
setData,
pageRouteStore,
location,
action,
});
});
return {
pushFnList,
popFnList,
};
};
const clearFn = () => {
if (!window.pageRouteStore[pathName]) {
return;
}
window.pageRouteStore[pathName].myunlistener?.();
window.pageRouteStore[pathName].myunlistener = null;
window.pageRouteStore[pathName] = null;
};
useEffect(() => {
if (!window.pageRouteStore) {
window.pageRouteStore = {};
}
if (!window.pageRouteStore?.[pathName]) {
window.pageRouteStore[pathName] = {};
}
window.pageRouteStore[pathName].myunlistener?.();
window.pageRouteStore[pathName].myunlistener = history.listen(
({ location, action }) => {
let hasInclude = pathIncludes.some((item) =>
location.pathname.includes(item),
);
// console.log('路径销毁判断', location.pathname, pathIncludes);
if (!hasInclude && action === 'PUSH') {
clearFn();
return;
}
const { pushFnList, popFnList } = initFn(pathObj);
if (action === 'POP') {
popFnList.forEach((fn) => {
// @ts-ignore
fn(location, action);
});
} else if (action === 'PUSH') {
pushFnList.forEach((fn) => {
// @ts-ignore
fn(location, action);
});
}
},
);
}, []);
return {
clearFn,
};
};
- 业务方使用
import { useRouterCache } from '@/hooks/useRouterCache';
const refTabData: any = {
current: null,
};
export const Cmp = () => {
useRouterCache({
pathObj: {
onPop({ getData }) {
// 页面进入的时候会取出数据设置到临时数据保存变量上
refTabData.current = getData()?.tab;
},
onPush({ setData }) {
// 页面离开的时候,会将变更的数据做一个保存
setData({
tab: refTabData.current,
});
},
},
// 在导入和详情这些页面保持监听器的激活,否则会销毁监听器,pathName是当前页面路由,import是导入的页面路由
pathIncludes: [pathName, 'import'],
});
useEffect(() => {
// 页面销毁清空临时数据
return () => {
refTabData.current = null;
};
}, []);
let defaultData = refTabData.current; // 获取记录的默认数据
const onChange = () => {
// 记录更新后的数据
refTabData.current = { name: 123 }
}
}
参考文档: