使用react-activation實(shí)現(xiàn)keepAlive支持返回傳參
介紹
這個(gè)項(xiàng)目是一個(gè)商城的后臺管理系統(tǒng),用umi2.0搭建,狀態(tài)管理使用dva,想要實(shí)現(xiàn)類似vue keep-alive的效果。
具體表現(xiàn)為:
從列表頁A跳轉(zhuǎn)A的詳情頁,列表頁A緩存
- 詳情頁沒做任何操作,跳回列表頁A,列表頁A不刷新,列表頁A頁碼不變
- 詳情頁進(jìn)行了編輯操作,跳回列表頁A,列表頁A刷新,列表頁A頁碼不變
- 詳情頁進(jìn)行了新建操作,跳回列表頁A,列表頁A刷新,列表頁A頁碼變?yōu)?
從列表頁A跳轉(zhuǎn)列表頁B,列表頁A不緩存
總結(jié)就是,一個(gè)頁面只有跳轉(zhuǎn)指定頁面的時(shí)候才緩存,并且當(dāng)返回這個(gè)被緩存的頁面時(shí),可以控制是否刷新。
代碼
1、安裝react-activation
"react-activation": "^0.10.2",
2、給路由增加meta
這個(gè)項(xiàng)目使用的是集中式配置路由,我增加了meta屬性,meta.keepAlive存在表示這是一個(gè)需要被keepAlive的路由,meta.keepAlive.toPath表示只有當(dāng)前往這個(gè)路由的時(shí)候,需要緩存
const routes = [ ... { name: '商品管理(商城商品)', path: '/web/supplier/goods/mallgoodsmgr', component: './supplier/goods/goodsManage', meta: { keepAlive: { toPath: '/web/supplier/goods/mallgoodsmgr/detail', // 只有去詳情頁的時(shí)候 才需要緩存 商品管理(商城商品)這個(gè)路由 }, }, } ... ]
3、根組件中渲染
在根組件中,用<AliveScope/>包裹整個(gè)應(yīng)用,用<KeepAlive/>包裹需要緩存的頁面。文檔中這部分寫在<App/>中,如果是umi可以寫在layouts里。
通過tree的扁平化計(jì)算獲取全部的帶有meta.keepAlive的routes:keepAliveRoutes,通過location.pathname判斷,如果當(dāng)前頁面是需要keepAlive的,那么就需要用<KeepAlive/>包裹。
import KeepAlive, { AliveScope, useAliveController } from 'react-activation' // tree扁平化 function treeToList(tree, childrenKey = 'routes') { ? var queen = [] ? var out = [] ? queen = queen.concat(tree) ? while (queen.length) { ? ? var first = queen.shift() ? ? if (first[childrenKey]) { ? ? ? queen = queen.concat(first[childrenKey]) ? ? ? delete first[childrenKey] ? ? } ? ? out.push(first) ? } ? return out } // 從routes路由tree里,拿到所有meta.keepAlive的路由:keepAliveRoutes const allFlatRoutes = treeToList(routes) // 所有路由 const keepAliveRoutes = allFlatRoutes.filter((item) => item.meta?.keepAlive) // keepAlive的路由 function Index(props) { ? const location = useLocation() ?? ? const routeItem = keepAliveRoutes.find( ? ? (item) => item.path == location.pathname ? ) // from 頁面 ? let dom = props.children ? if (routeItem) { ? ? dom = <KeepAlive id={location.pathname}>{props.children}</KeepAlive> // id 一定要加 否則 keepAlive的頁面 跳轉(zhuǎn) 另一個(gè)keepAlive的頁面 會有問題 ? } ? return ( ? ? <AliveScope> ? ? ? <div className={styles.page_container}>{dom}</div> ? ? </AliveScope> ? ) }
注意AliveScope中包含多個(gè)KeepAlive的話,<KeepAlive/>一定要帶id。
4、跳轉(zhuǎn)指定頁面的時(shí)候才緩存
上一步之后,頁面雖然被緩存,但是它跳轉(zhuǎn)任何頁面都會緩存,我們需要只有跳轉(zhuǎn)指定頁面的時(shí)候才緩存。
我的方法是
如果跳轉(zhuǎn)的頁面正好是它自己的meta.keepAlive.toPath,那就不做任何操作(因?yàn)榇藭r(shí)本頁面已經(jīng)被KeepAlive包裹了,處于緩存的狀態(tài))
如果不是它自己的meta.keepAlive.toPath,調(diào)用clear方法,清空緩存
4.1 clear方法
react-activation提供useAliveController可以手動(dòng)控制緩存,其中clear方法用于清空所有緩存中的 KeepAlive
4.2 用狀態(tài)管理記錄toPath
監(jiān)聽history,用狀態(tài)管理(我用的dva)記錄即將前往的頁面(下一個(gè)頁面)toPath
我通過dva記錄應(yīng)用即將前往的頁面
const GlobalModel = { ? namespace: 'global', ? state: { ? ? /** ? ? ?* keepAlive ? ? ?*/ ? ? toPath: '', ? ? keepAliveOptions: {}, // 給keepAlive的頁面 傳的options ? }, ? effects: {}, ? reducers: { ? ? save(state, { payload }) { ? ? ? return { ? ? ? ? ...state, ? ? ? ? ...payload, ? ? ? } ? ? }, ? ? setToPath(state, { payload }) { ? ? ? return { ? ? ? ? ...state, ? ? ? ? toPath: payload, ? ? ? } ? ? }, ? }, ? subscriptions: { ? ? setup({ history, dispatch }) { ? ? ? // Subscribe history(url) change, trigger `load` action if pathname is `/` ? ? ? history.listen((route, typeStr) => { ? ? ? ? const { pathname } = route ? ? ? ? dispatch({ ? ? ? ? ? type: 'setToPath', ? ? ? ? ? payload: pathname, ? ? ? ? }) ? ? ? }) ? ? }, ? }, }
4.3 給根組件增加useEffect
根組件從dva中讀取即將訪問的頁面toPath,然后加一個(gè)useEffect,如果即將前往的頁面不是當(dāng)前路由自己的meta.keepAlive.toPath,就執(zhí)行react-activation提供的clear方法
... function Index(props) { ? const location = useLocation() ? const toPath = props.global.toPath // 從dva中拿到 將要訪問的頁面 ?? ? const routeItem = keepAliveRoutes.find( ? ? (item) => item.path == location.pathname ? ) // from 頁面 ?? ?? ? /// 新加代碼 ? /// 新加代碼 ? /// 新加代碼 ? useEffect(() => { ? ? console.log('toPath改變', toPath) ? ? // from頁面 是需要keepAlive的頁面 ? ? if (routeItem) { ? ? ? console.log('from頁面 是需要keepAlive的頁面', routeItem) ? ? ? if (toPath == routeItem.meta?.keepAlive.toPath) { ? ? ? ? // 所去的 頁面 正好是當(dāng)前這個(gè)路由的 keepAlive.toPath ? ? ? ? console.log('所去的 頁面 正好是當(dāng)前這個(gè)路由的 keepAlive.toPath,不做什么') ? ? ? } else { ? ? ? ? console.log('clear') ? ? ? ? if (aliveController?.clear) { ? ? ? ? ? aliveController.clear() ? ? ? ? } ? ? ? } ? ? } ? }, [toPath]) ? /// 新加代碼 end ? let dom = props.children ? if (routeItem) { ? ? dom = <KeepAlive id={location.pathname}>{props.children}</KeepAlive> // id 一定要加 否則 keepAlive的頁面 跳轉(zhuǎn) 另一個(gè)keepAlive的頁面 會有問題 ? } ? return ( ? ? <AliveScope> ? ? ? <div className={styles.page_container}>{dom}</div> ? ? </AliveScope> ? ) } export default connect(({ global, login }) => ({ global, login }))(Index)
4.4 優(yōu)化
現(xiàn)在有一個(gè)問題:從列表A跳轉(zhuǎn)詳情頁,然后跳轉(zhuǎn)列表B,再跳轉(zhuǎn)列表A的時(shí)候,A是不刷新的:
列表A => 詳情頁 => 列表B => 列表A 此時(shí)列表A不刷新或者空白。
因?yàn)閺脑斍轫摮鰜恚ㄌD(zhuǎn)列表B)的時(shí)候,我們沒有清空列表A的緩存。
所以要檢查當(dāng)前頁面是否是某個(gè)需要keepAlive頁面的toPath頁面
根組件:
function Index(){ ? ... ?? ? const parentItem = keepAliveRoutes.find((item) => item.meta?.keepAlive?.toPath == location.pathname) // parentItem存在表示 當(dāng)前頁面 是某個(gè)keepAlive的頁面 的toPath ? useEffect(() => { ? ? console.log('toPath改變', toPath) ? ? ... ? ?? ? ? /// 新加代碼 ? ? /// 新加代碼 ? ? /// 新加代碼 ? ? // from頁面 是某個(gè)keepAlive的頁面 的toPath ? ? if (parentItem) { ? ? ? console.log('from頁面 是某個(gè)keepAlive的頁面 的toPath,parentItem', parentItem) ? ? ? if (toPath == parentItem.path) { ? ? ? ? // 所去的 頁面是 parentItem.path ? ? ? ? console.log('所去的 頁面是 parentItem.path,不做什么') ? ? ? } else { ? ? ? ? console.log('clear') ? ? ? ? if (aliveController?.clear) { ? ? ? ? ? aliveController.clear() ? ? ? ? } ? ? ? } ? ? } ? }, [toPath]) ?? ? ... }
5、抽離邏輯到自定義hooks
useKeepAliveLayout.js
import { useEffect } from 'react' import { useLocation } from 'react-router-dom' import KeepAlive, { AliveScope, useAliveController } from 'react-activation' import routes from '../../config/router.config' // tree扁平化 function treeToList(tree, childrenKey = 'routes') { ? var queen = [] ? var out = [] ? queen = queen.concat(tree) ? while (queen.length) { ? ? var first = queen.shift() ? ? if (first[childrenKey]) { ? ? ? queen = queen.concat(first[childrenKey]) ? ? ? delete first[childrenKey] ? ? } ? ? out.push(first) ? } ? return out } const allFlatRoutes = treeToList(routes) // 所有路由 const keepAliveRoutes = allFlatRoutes.filter((item) => item.meta?.keepAlive) // keepAlive的路由 function index(props) { ? const location = useLocation() ? // keep alive ? const aliveController = useAliveController() ? const toPath = props.global.toPath // 將要訪問的頁面 ? const routeItem = keepAliveRoutes.find((item) => item.path == location.pathname) // from 頁面 ? const parentItem = keepAliveRoutes.find((item) => item.meta?.keepAlive?.toPath == location.pathname) ? useEffect(() => { ? ? console.log('toPath改變', toPath) ? ? // from頁面 是需要keepAlive的頁面 ? ? if (routeItem) { ? ? ? console.log('from頁面 是需要keepAlive的頁面', routeItem) ? ? ? if (toPath == routeItem.meta?.keepAlive.toPath) { ? ? ? ? // 所去的 頁面 正好是當(dāng)前這個(gè)路由的 keepAlive.toPath ? ? ? ? console.log('所去的 頁面 正好是當(dāng)前這個(gè)路由的 keepAlive.toPath,不做什么') ? ? ? } else { ? ? ? ? console.log('clear') ? ? ? ? if (aliveController?.clear) { ? ? ? ? ? aliveController.clear() ? ? ? ? } ? ? ? } ? ? } ? ? // from頁面 是某個(gè)keepAlive的頁面 的toPath ? ? if (parentItem) { ? ? ? console.log('from頁面 是某個(gè)keepAlive的頁面 的toPath,parentItem', parentItem) ? ? ? if (toPath == parentItem.path) { ? ? ? ? // 所去的 頁面是 parentItem.path ? ? ? ? console.log('所去的 頁面是 parentItem.path,不做什么') ? ? ? } else { ? ? ? ? console.log('clear') ? ? ? ? if (aliveController?.clear) { ? ? ? ? ? aliveController.clear() ? ? ? ? } ? ? ? } ? ? } ? }, [toPath]) ? return { ? ? fromIsNeedKeepAlive: routeItem, ? } } export default index
根組件只需要引入這個(gè)hooks就可以了:
function Index(props) { ? const location = useLocation() ? const { fromIsNeedKeepAlive } = useKeepAliveLayout(props) // 關(guān)鍵代碼關(guān)鍵代碼關(guān)鍵代碼 ? let dom = props.children ? if (fromIsNeedKeepAlive) { ? ? dom = <KeepAlive id={location.pathname}>{props.children}</KeepAlive> // id 一定要加 否則 keepAlive的頁面 跳轉(zhuǎn) 另一個(gè)keepAlive的頁面 會有問題 ? } ? return ( ? ? <AliveScope> ? ? ? <div className={styles.page_container}>{dom}</div> ? ? </AliveScope> ? ) }
6、 從詳情頁返回列表頁的時(shí)候,控制列表頁是否刷新,即返回傳參
現(xiàn)在只剩下這最后一個(gè)問題了,其實(shí)就是keepAlive的頁面,goBack傳參的問題
思路:
- 狀態(tài)管理中增加一個(gè)keepAliveOptions對象,這就是詳情頁給列表頁傳的參數(shù)
- 詳情頁執(zhí)行g(shù)oBack的時(shí)候,調(diào)用狀態(tài)管理dispatch修改keepAliveOptions
- 列表頁監(jiān)聽keepAliveOptions,如果keepAliveOptions改變就執(zhí)行傳入的方法
useKeepAliveOptions.js
import { useEffect } from 'react' import { useDispatch, useStore } from 'dva' import { router } from 'umi' /** ?* @description keepAlive的頁面,當(dāng)有參數(shù)傳過來的時(shí)候,可以用這個(gè)監(jiān)聽到 ?* @param {(options:object)=>void} func ?*/ export function useKeepAlivePageShow(func) { ? const dispatch = useDispatch() ? const store = useStore() ? const state = store.getState() ? const options = state.global.keepAliveOptions ?? {} ? useEffect(() => { ? ? func(options) // 執(zhí)行 ? ? return () => { ? ? ? console.log('keepAlive頁面 的緩存 卸載') ? ? ? dispatch({ ? ? ? ? type: 'global/save', ? ? ? ? payload: { ? ? ? ? ? keepAliveOptions: {}, ? ? ? ? }, ? ? ? }) ? ? } ? }, [JSON.stringify(options)]) } /** ?* @description PageA(keepAlive的頁面)去了 PageB, 當(dāng)從PageB goBack,想要給PageA傳參的時(shí)候,需要使用這個(gè)方法 ?* @returns {(params:object)=>void} ?*/ export function useKeepAliveGoback() { ? const dispatch = useDispatch() ? function goBack(parmas = {}) { ? ? dispatch({ ? ? ? type: 'global/save', ? ? ? payload: { ? ? ? ? keepAliveOptions: parmas, ? ? ? }, ? ? }) ? ? router.goBack() ? } ? return goBack }
使用:
詳情頁
import { useKeepAliveGoback } from '@/hooks/useKeepAliveOptions' function Index(){ ? ? ... ? ? const keepAliveGoback = useKeepAliveGoback() // 用于給上一頁keepAlive的頁面 傳參 ? ? ... ? ?? ? ? return ( ? ? ? ? <> ? ? ? ? ? ? ... ? ? ? ? ? ? <button onClick={() => { ? ? ? ? ? ? ? ? keepAliveGoback({ isAddSuccess: true }) // 給列表頁傳options ? ? ? ? ? ? }></button> ? ? ? ? ? ? ... ? ? ? ? </> ? ? ) }
列表頁
import { useKeepAlivePageShow } from '@/hooks/useKeepAliveOptions' function Index(){ ? ? ... ? ? // options: isAddSuccess isEditSuccess ? ? useKeepAlivePageShow((options) => { ? ? ? ? console.log('keepAlive options', options) ? ? ? ? if (options.isAddSuccess) { ? ? ? ? ? // 新建成功 // 列表頁碼變?yōu)? 并且刷新 ? ? ? ? ? search() ? ? ? ? } else if (options.isEditSuccess) { ? ? ? ? ? // 編輯成功 // 列表頁碼不變 并且刷新 ? ? ? ? ? getData() ? ? ? ? } ? ? }) ? ?? ? ? ... ? ?? ? ? return <>...</> }
相關(guān)文檔
到此這篇關(guān)于使用react-activation實(shí)現(xiàn)keepAlive支持返回傳參的文章就介紹到這了,更多相關(guān)react-activation keepAlive返回傳參內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
從零開始學(xué)習(xí)搭建React腳手架項(xiàng)目
這篇文章主要介紹了從零開始學(xué)習(xí)搭建React腳手架項(xiàng)目,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-08-08如何去除富文本中的html標(biāo)簽及vue、react、微信小程序中的過濾器
這篇文章主要介紹了如何去除富文本中的html標(biāo)簽及vue、react、微信小程序中的過濾器,在vue及react中經(jīng)常會遇到,今天通過實(shí)例代碼給大家講解,需要的朋友可以參考下2018-11-11react?fiber使用的關(guān)鍵特性及執(zhí)行階段詳解
這篇文章主要為大家介紹了react?fiber使用的關(guān)鍵特性及執(zhí)行階段詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-05-05淺談react-router HashRouter和BrowserRouter的使用
本篇文章主要介紹了淺談react-router HashRouter和BrowserRouter的使用,具有一定的參考價(jià)值,有興趣的可以了解一下2017-12-12使用React?Hooks模擬生命周期的實(shí)現(xiàn)方法
這篇文章主要介紹了使用React?Hooks模擬生命周期,本文舉例說明如何使用 hooks 來模擬比較常見的 class 組件生命周期,需要的朋友可以參考下2023-02-02詳解在React項(xiàng)目中如何集成和使用web worker
在復(fù)雜的React應(yīng)用中,某些計(jì)算密集型或耗時(shí)操作可能會阻塞主線程,導(dǎo)致用戶界面出現(xiàn)卡頓或響應(yīng)慢的現(xiàn)象,為了優(yōu)化用戶體驗(yàn),可以采用Web Worker來在后臺線程中執(zhí)行這些操作,本文將詳細(xì)介紹在React項(xiàng)目中如何集成和使用Web Worker來改善應(yīng)用性能,需要的朋友可以參考下2023-12-12