keepAlive实现前端页面和组件缓存

314 阅读2分钟
import React, { ReactNode } from 'react';
//引入react-activation插件中的KeepAlive,重新命名为KeepAliveComp;
import { KeepAlive as KeepAliveComp, useAliveController } from 'react-activation';   
//暴露react-activation中的钩子函数和方法
import { keepAliveProps } from '../utils/keepAliveUtil';

export type IKeepAliveProps = {
    children?: ReactNode | ReactNode[];
    name?: string;
    pathname: string;
    id?:string;
    cacheKey?: string;
    when?: boolean | (() => boolean);
    location?: any;
}

//包裹页面的组件
export const KeepAlive = (props: IKeepAliveProps) => {
    return (
        <KeepAliveComp {...keepAliveProps(props)}>
            <>
                {props?.children}
            </>
        </KeepAliveComp>
    )
}
//keepAliveUtil.ts  组件

import { useAliveController } from 'react-activation';
const sessionKey = 'usmcc_keepalive_tabskey';
export const keepAliveProps = (props) => {
    const { isOnlyLastCache = false, pathname = '', id = '' } = props;
    if (pathname) {
        const currentPathname = pathname;
        const sysCode = sessionStorage.getItem('sysCode') || ''
        const sysRoutes = sysCode && sessionStorage.getItem(sysCode + '-routes')  //获取存储的路由列表数据
        let isFlagData = true;
        let isCache = true;   //是否需要缓存
        const currentId = getCurrentId(props);   //当前缓存页面的id
        if (sysRoutes) {
            const getPathNames = JSON.parse(sysRoutes).panes;
            const currentKey = getPathNames?.find(item => item.pathname == currentPathName)?.key;    //当前最新的tabkey值
            const getSessionTabsValues = sessionStorage.getItem(sessionKey) || '{}'   //获取缓存的tabkey对象数据
            let sessionTabsValueObj = JSON.parse(getSessionTabsValues);
            const getSessionTabValue = sessionTabsValueObj[currentId];
            //比较缓存的tabkey值是否和系统中对应路由下的currentKey是否一致,false:是更新当前页面缓存,true:当前页面缓存不变;getSessionTabValue 没有缓存时,取值true,否则执行后面逻辑是  当前tab和缓存tab不一致
            isFlagData = !getSessionTabValue || (getSessionTabValue && (getSessionTabValue !== currentKey)) ? false : true;
            isCache = !getSessionTabValue || isFlagData;   //首次getSessionTabValue没有缓存时,需要缓存;
            //isFlagData  false表示当前tab标签key值有变化,更新当前页缓存
            if (!isFlagData) {
                const { refreshScope } = useAliveController()
                //刷新缓存的数据,解决关闭标签后重新打开页面UI没有变化
                refreshScope && refreshScope(pathname + id)
                //清除原来缓存的标签key值,重新保存;旧的tabkey值 getSessionTabValue对应的旧数据清除掉
                if (getSessionTabsValues && getSessionTabsValues != '{}') {
                    Object.entries(sessionTabsValueObj)?.forEach((item) => {
                        if (getSessionTabValue) {
                            if (item && (getSessionTabValue === item[1])) {
                                delete sessionTabsValueObj[item[0]]  //清除旧tabkey数据
                            }
                        } else {
                        //isOnlyLastCache true:相同路由,不同id情况下,仅使用最后一次缓存,清除原来缓存中tabkey相同的数据
                        if (isOnlyLastCache && item && (currentKey === item[1])) {
                            delete sessionTabsValueObj[item[0]]  //清除旧tabkey数据
                        }
                    })
                }
            }
            //存储当前的tab的标签key,因为删除后的标签key值会发生变化
            getSessionTabsValueObject= {...getSessionTabsValueObj, [currentId]: currentKey}
            const sessionTabsValueJson = JSON.stringify(sessionTabsValueObj)
            sessionStorage.setItem(sessionKey, sessionTabsValue)
        }
        return {
            name: pathname + id,
            id: currentId,
            when: () => isCache
        }
    }
    return {
        name: pathname + id,
        id: currentId,
        when: () => isCache
    }
}
interface IQuery {
    [key: string]: string | any;
}
//获取当前页面路由的参数
const getQuery = () => {
    const hash = window.location.hash;
    const params = hash && hash.split('?')[1];
    let obj: IQuery = {};
    let arr = params &&  params.split('&');
    if (arr) {
        arr.map(item => {
            let arrNew = item.split('=');
            obj[arrNew[0]] = arrNew[1]
        })
    }
    return obj;
}
//当前缓存页面的id
const getCurrentId = (props) => {
    let currentId = props?.id || '';
    const currentPathName = props?.pathname;
    if (!currentPathName)
    return currentId;
    const query = getQuery();
    const queryStr = query && JSON.stringify(query) !== '{}' ? JSON.stringify(query) : ''
    //有参数,判断当前路径带的参数是否在对应sessionStore存储的相同路由下
    if (queryStr) {
        const menuParams = query?.menuType ? (query?.nodeName + query?.menuType) + '_' : ''
        currentId = currentPathName + '_' + menuParams + currentId;
    } else {
        currentId = currentPathName + '_' + currentId
    }
    return currentId;
}

demo1:路由配置中添加缓存(首选)

import { KeepAlive } from 'usmcc';

//缓存所有组件或者页面
<Route path='/list1' render={
    props => (
       <KeepAlive pathname={props.location?.pathname}>
           <List1 {...props}>
       </KeepAlive>
    )
} exact>

//只缓存最后一次组件或者页面,需要传入唯一参数id,和isOnlyLastCache={true}
<Route path='/list1' render={
    props => (
       <KeepAlive pathname={props.location?.pathname} id = {props.location?.query?.id} isOnlyLastCache={true}>
           <List1 {...props}>
       </KeepAlive>
    )
} exact>

demo2:页面或者组件中添加缓存

import { KeepAlive } from 'usmcc';

//缓存所有组件或者页面
<KeepAlive pathname={props.location?.pathname}>
    <List1/>   //业务组件
</KeepAlive>

//只缓存最后一次组件或者页面,需要传入唯一参数id,和isOnlyLastCache={true}
<KeepAlive pathname={props.location?.pathname} id = {props.location?.query?.id} isOnlyLastCache={true}>
    <List1/>   //业务组件
</KeepAlive>