網站首頁 編程語言 正文
介紹
這個項目是一個商城的后臺管理系統,用umi2.0搭建,狀態管理使用dva,想要實現類似vue keep-alive的效果。
具體表現為:
從列表頁A跳轉A的詳情頁,列表頁A緩存
- 詳情頁沒做任何操作,跳回列表頁A,列表頁A不刷新,列表頁A頁碼不變
- 詳情頁進行了編輯操作,跳回列表頁A,列表頁A刷新,列表頁A頁碼不變
- 詳情頁進行了新建操作,跳回列表頁A,列表頁A刷新,列表頁A頁碼變為1
從列表頁A跳轉列表頁B,列表頁A不緩存
總結就是,一個頁面只有跳轉指定頁面的時候才緩存,并且當返回這個被緩存的頁面時,可以控制是否刷新。
代碼
1、安裝react-activation
"react-activation": "^0.10.2",
2、給路由增加meta
這個項目使用的是集中式配置路由,我增加了meta屬性,meta.keepAlive存在表示這是一個需要被keepAlive的路由,meta.keepAlive.toPath表示只有當前往這個路由的時候,需要緩存
const routes = [
...
{
name: '商品管理(商城商品)',
path: '/web/supplier/goods/mallgoodsmgr',
component: './supplier/goods/goodsManage',
meta: {
keepAlive: {
toPath: '/web/supplier/goods/mallgoodsmgr/detail', // 只有去詳情頁的時候 才需要緩存 商品管理(商城商品)這個路由
},
},
}
...
]
3、根組件中渲染
在根組件中,用<AliveScope/>包裹整個應用,用<KeepAlive/>包裹需要緩存的頁面。文檔中這部分寫在<App/>中,如果是umi可以寫在layouts里。
通過tree的扁平化計算獲取全部的帶有meta.keepAlive的routes:keepAliveRoutes,通過location.pathname判斷,如果當前頁面是需要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的頁面 跳轉 另一個keepAlive的頁面 會有問題
? }
? return (
? ? <AliveScope>
? ? ? <div className={styles.page_container}>{dom}</div>
? ? </AliveScope>
? )
}
注意AliveScope中包含多個KeepAlive的話,<KeepAlive/>一定要帶id。
4、跳轉指定頁面的時候才緩存
上一步之后,頁面雖然被緩存,但是它跳轉任何頁面都會緩存,我們需要只有跳轉指定頁面的時候才緩存。
我的方法是
如果跳轉的頁面正好是它自己的meta.keepAlive.toPath,那就不做任何操作(因為此時本頁面已經被KeepAlive包裹了,處于緩存的狀態)
如果不是它自己的meta.keepAlive.toPath,調用clear方法,清空緩存
4.1 clear方法
react-activation提供useAliveController可以手動控制緩存,其中clear方法用于清空所有緩存中的 KeepAlive
4.2 用狀態管理記錄toPath
監聽history,用狀態管理(我用的dva)記錄即將前往的頁面(下一個頁面)toPath
我通過dva記錄應用即將前往的頁面
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,然后加一個useEffect,如果即將前往的頁面不是當前路由自己的meta.keepAlive.toPath,就執行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) {
? ? ? ? // 所去的 頁面 正好是當前這個路由的 keepAlive.toPath
? ? ? ? console.log('所去的 頁面 正好是當前這個路由的 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的頁面 跳轉 另一個keepAlive的頁面 會有問題
? }
? return (
? ? <AliveScope>
? ? ? <div className={styles.page_container}>{dom}</div>
? ? </AliveScope>
? )
}
export default connect(({ global, login }) => ({ global, login }))(Index)
4.4 優化
現在有一個問題:從列表A跳轉詳情頁,然后跳轉列表B,再跳轉列表A的時候,A是不刷新的:
列表A => 詳情頁 => 列表B => 列表A 此時列表A不刷新或者空白。
因為從詳情頁出來(跳轉列表B)的時候,我們沒有清空列表A的緩存。
所以要檢查當前頁面是否是某個需要keepAlive頁面的toPath頁面
根組件:
function Index(){
? ...
??
? const parentItem = keepAliveRoutes.find((item) => item.meta?.keepAlive?.toPath == location.pathname) // parentItem存在表示 當前頁面 是某個keepAlive的頁面 的toPath
? useEffect(() => {
? ? console.log('toPath改變', toPath)
? ? ...
? ??
? ? /// 新加代碼
? ? /// 新加代碼
? ? /// 新加代碼
? ? // from頁面 是某個keepAlive的頁面 的toPath
? ? if (parentItem) {
? ? ? console.log('from頁面 是某個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) {
? ? ? ? // 所去的 頁面 正好是當前這個路由的 keepAlive.toPath
? ? ? ? console.log('所去的 頁面 正好是當前這個路由的 keepAlive.toPath,不做什么')
? ? ? } else {
? ? ? ? console.log('clear')
? ? ? ? if (aliveController?.clear) {
? ? ? ? ? aliveController.clear()
? ? ? ? }
? ? ? }
? ? }
? ? // from頁面 是某個keepAlive的頁面 的toPath
? ? if (parentItem) {
? ? ? console.log('from頁面 是某個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
根組件只需要引入這個hooks就可以了:
function Index(props) {
? const location = useLocation()
? const { fromIsNeedKeepAlive } = useKeepAliveLayout(props) // 關鍵代碼關鍵代碼關鍵代碼
? let dom = props.children
? if (fromIsNeedKeepAlive) {
? ? dom = <KeepAlive id={location.pathname}>{props.children}</KeepAlive> // id 一定要加 否則 keepAlive的頁面 跳轉 另一個keepAlive的頁面 會有問題
? }
? return (
? ? <AliveScope>
? ? ? <div className={styles.page_container}>{dom}</div>
? ? </AliveScope>
? )
}
6、 從詳情頁返回列表頁的時候,控制列表頁是否刷新,即返回傳參
現在只剩下這最后一個問題了,其實就是keepAlive的頁面,goBack傳參的問題
思路:
- 狀態管理中增加一個keepAliveOptions對象,這就是詳情頁給列表頁傳的參數
- 詳情頁執行goBack的時候,調用狀態管理dispatch修改keepAliveOptions
- 列表頁監聽keepAliveOptions,如果keepAliveOptions改變就執行傳入的方法
useKeepAliveOptions.js
import { useEffect } from 'react'
import { useDispatch, useStore } from 'dva'
import { router } from 'umi'
/**
?* @description keepAlive的頁面,當有參數傳過來的時候,可以用這個監聽到
?* @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) // 執行
? ? return () => {
? ? ? console.log('keepAlive頁面 的緩存 卸載')
? ? ? dispatch({
? ? ? ? type: 'global/save',
? ? ? ? payload: {
? ? ? ? ? keepAliveOptions: {},
? ? ? ? },
? ? ? })
? ? }
? }, [JSON.stringify(options)])
}
/**
?* @description PageA(keepAlive的頁面)去了 PageB, 當從PageB goBack,想要給PageA傳參的時候,需要使用這個方法
?* @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) {
? ? ? ? ? // 新建成功 // 列表頁碼變為1 并且刷新
? ? ? ? ? search()
? ? ? ? } else if (options.isEditSuccess) {
? ? ? ? ? // 編輯成功 // 列表頁碼不變 并且刷新
? ? ? ? ? getData()
? ? ? ? }
? ? })
? ??
? ? ...
? ??
? ? return <>...</>
}
相關文檔
react-activation
dva文檔
原文鏈接:https://juejin.cn/post/7098602213102059527
相關推薦
- 2023-04-29 Linux?grep?-q用法示例詳解_linux shell
- 2022-12-31 go操作Kafka使用示例詳解_Golang
- 2022-08-03 python中filter,map,reduce的作用_python
- 2022-08-27 golang?隨機數的兩種方式_Golang
- 2022-01-30 判斷element Ui 表格(el-table)中復選框中的選中狀態
- 2022-12-06 靜態pod?創建使用示例詳解_docker
- 2022-09-09 python中對開區間和閉區間的理解_python
- 2023-07-09 SQL Server中的NULL值處理:判斷與解決方案
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支