這里寫目錄標題
- 一、安裝插件(可選)
- 1、react-activation (推薦)
- 2、umi-plugin-keep-alive
- 二、AliveScope的兩種配置方式
- 1、在src/app.ts 中配置
- 2、在src/layout/index.tsx中配置
- 三、umi中的配置
- 四、使用問題記錄
- 1、`drop`使用不生效,使用`setTimeout`
- 2、`refresh`在頁面mounted之前執行
- 參考
一、安裝插件(可選)
1、react-activation (推薦)
npm install react-activation
2、umi-plugin-keep-alive
這個插件也是基于react-activation
npm install umi-plugin-keep-alive --save
使用:
import { KeepAlive } from 'umi'
const contentList = () => {return (<KeepAlive name="/About" //可按照name卸載緩存狀態下的 <KeepAlive> 節點saveScrollPosition="screen" //自動保存共享屏幕容器的滾動位置when={true} > //true卸載時緩存,false卸載時不緩存{/*要保存狀態的組件*/}</KeepAlive>)
}
但我使用umi4
搭建的項目,在安裝該插件后,umi沒有導出KeepAlive
,無法使用,所以直接用的第一種
二、AliveScope的兩種配置方式
保證AliveScope穩定
1、在src/app.ts 中配置
配置文檔: https://umijs.org/docs/api/runtime-config
import React from 'react';
import { AliveScope } from 'react-activation';// 修改交給 react-dom 渲染時的根組件, 在外層包裹AliveScope
export function rootContainer(container: any) {return React.createElement(AliveScope, null, container);
}
然后在需要的組件外層包裹KeepAlive
import KeepAlive from "react-activation";
function Test() {return (<KeepAlive>{/*頁面組件*/}</KeepAlive>);
}// 如果有connect函數的可以如下寫法:
const DataMine = connect(({ user }) => ({ user }))(Mine)
export default () => <KeepAlive><DataMine/></KeepAlive>;
2、在src/layout/index.tsx中配置
export default function Layout(props: any) {return (<AliveScope>{/*頁面組件*/}</AliveScope>)
}
三、umi中的配置
umi 的 babel 配置要加在config.js文件中
extraBabelPlugins: ['react-activation/babel']
四、使用問題記錄
1、一個組件設置keepAlive,它的某個子組件A也設置KeepAlive, 跳轉到其他頁面再跳回該頁面,A不見了
2、在某些組件不需要保活的時候,通過drop、refresh等方式去清掉某個緩存,這里要注意:
1、drop
使用不生效,使用setTimeout
import { useAliveController } from 'react-activation';
const { drop, dropScope, refresh } = useAliveController();// 示例一: 要在跳轉該頁面之前執行,需要延時, 去掉setTimeout,drop不生效
drop('testName');
setTimeout(() => {history.push('/test');
}, 50);// 示例二: 在useUnactivate中使用
useUnactivate(() => {
// 在某個條件下退出要清除緩存if(xxxx) {setTimeout(() => dropScope('testName'), 0)}
})
2、refresh
在頁面mounted之前執行
refresh('testName')
但這里有個小坑,refresh會重新刷新頁面,但是不會重新去拿頁面的url,也就是說如果你的某些邏輯依賴于url的query字段,需要修改一下獲取
import { useSearchParams, history } from 'umi';// 原本代碼
const Test = (props) => {const [params] = useSearchParams();// params.get('xxx') 獲取參數去處理
};// 修改為:
const Test = (props) => {const url = window.location.href;const params = (new URL(url)).searchParams;// params.get('xxx') 獲取參數去處理
};
3、滾動條的位置問題
當在兩個已經緩存的頁面跳轉時,滾動條位置是保持的,
但當從一個頁面去到一個未保活的頁面,第一次跳轉原頁面的滾動條位置未保持,第二次跳轉才保持
// KeepAlive組件增加saveScrollPosition=參數
<KeepAlive saveScrollPosition={true} />// 如果組件共享了屏幕滾動容器如 document.body 或 document.documentElement, 將 saveScrollPosition 屬性設置為 “screen”<KeepAlive saveScrollPosition="screen" />
解決方案
自定義keep.ts
方法,監聽每個頁面的滾動位置,并在回到該頁面時滾回上次記錄點。
import _ from 'lodash';
import { history } from 'umi';interface PageItem {ele: Element; // 元素y: number; // 豎向位置
}
interface PageLocationProps {[key: string]: PageItem;
}
// 獲取初始的路由
let pName = history.location.pathname;// 存儲每個頁面的滾動ele和top距離
let scrollLoction: PageLocationProps = {};// 滾動記錄頁面的ele和距離
const handleScroll = _.debounce((e) => {if (!scrollLoction[pName]) {scrollLoction[pName] = {} as PageItem;}scrollLoction[pName].ele = e.target;scrollLoction[pName].y = e.target.scrollTop;},500,{ leading: false, trailing: true },
);// 路由監聽,判斷是否回滾到之前的位置
const unlisten = history.listen((route) => {pName = route.location.pathname;if (pName in scrollLoction) {// 在頁面加載出來之后,放在宏任務中即可, 時間為0也是可以的setTimeout(() => {// 如果相等,就不需要在再次scrollToif (scrollLoction[pName] && scrollLoction[pName].ele.scrollTop !== scrollLoction[pName].y) {// console.log('手動滾動', scrollLoction[pName].y);scrollLoction[pName].ele.scrollTo(0, scrollLoction[pName].y);}}, 300);}
});// 在pc端可以,在移動端發現onload,和onbeforeunload監聽都無效
// 添加監聽事件
window.addEventListener('scroll', handleScroll, true);// 卸載: 移除相關引用和監聽
window.addEventListener('beforeunload', () => {scrollLoction = {};unlisten();window.removeEventListener('scroll', handleScroll, true);
});
在app.ts中
// react-activation 從當前頁跳轉到未加載的頁面時,沒有保存位置, js修復
// 比如: 從看板詳情到指標詳情, 返回后,發現滾動位置沒有保存,但第二次進入指標詳情返回后有保存
import '@/utils/keep';
參考
react16路由緩存react-activation詳解