感谢该大佬提供的组件:GitHub - CJY0208/react-activation: Hack <KeepAlive /> for React

 react 里 keep-alive 的实现目前是黑科技,会有些问题

使用过程中遇到问题的话,可以优先看这儿

https://github.com/CJY0208/react-activation/blob/master/README_CN.md#breaking-change-%E7%94%B1%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86%E5%BC%95%E5%8F%91%E7%9A%84%E9%A2%9D%E5%A4%96%E9%97%AE%E9%A2%98

  • 安装依赖包 
yarn add react-activation
  • 配置plugin

1、因为我的项目是antd-design-pro,所以不需要按照组件大佬那样配置,只需要安装新的依赖 umi-plugin-keep-alive。antd-design-pro搭建的项目只需要这一步!!!

yarn add umi-plugin-keep-alive

 2、若是普通react,则按照大佬那样,在配置文件增加:

{
  "plugins": [
    "react-activation/babel"
  ]
}

或者如果这样报错的话,在config.js文件


  // umi 的 babel 配置要加在这儿
  extraBabelPlugins: ['react-activation/babel'],

  • 使用

比如你希望不让 Counter 卸载,那就包在 Counter 外头

  • 注意点

比如我使用的时候,加在PageContainer内,我的PageContainer添加了多页签,所以我希望切换页面,页面保留缓存。

{/* KeepAlive必须加上name和id,否则切换menu,页面不会变化 */}

        <PageContainer
          breadcrumb={'none'}
          title={false}
          // tabList={tabList}
          tabList={newTabListData}
          tabProps={{
            type: 'editable-card',
            hideAdd: true,
            onEdit: (e, action) => {
              if (action === 'remove') {
                remove(e);
              }
            },
          }}
          tabActiveKey={activeKey}
          // tabActiveKey={getTabKey()}
          onTabChange={handleTabChange}
        >
          {/* KeepAlive必须加上name和id,否则切换menu,页面不会变化 */}
          {/* {newTabListData?.length > 0 ? props.children : null} */}
          {newTabListData?.length > 0 ? (
            <KeepAlive
              when={true}
              name={props.children?.props?.location?.pathname}
              id={props.children?.props?.location?.pathname}
              saveScrollPosition="screen"
            >
              {props.children}
            </KeepAlive>
          ) : null}
        </PageContainer>
  • antd-design-pro切换菜单时,清除缓存

我实际使用的时候,在移除tabs时,才清除 

app.jsx

import { PageContainer, PageLoading } from '@ant-design/pro-layout';
import { useState, useEffect } from 'react';
import { history, useModel } from 'umi';
import RightContent from '@/components/RightContent';
import { currentUser as queryCurrentUser } from './services/ant-design-pro/api';
import { QuestionCircleOutlined } from '@ant-design/icons';
import KeepAlive, { withAliveScope, useAliveController } from 'react-activation';
import { getTab, isEmpty } from '../utils/common';
import styles from './pages/common.less';
import { Modal } from 'antd';
import { Link } from 'react-router-dom';
import customMenuDate from './customMenu';
const isDev = process.env.NODE_ENV === 'development';
const loginPath = '/user/login';
/** 获取用户信息比较慢的时候会展示一个 loading */

export const initialStateConfig = {
  loading: <PageLoading />,
};
/**
 * @see  https://umijs.org/zh-CN/plugins/plugin-initial-state
 * */

export async function getInitialState() {
  const fetchUserInfo = async () => {
    try {
      const msg = await queryCurrentUser();
      return msg.data;
    } catch (error) {
      history.push(loginPath);
    }

    return undefined;
  }; // 如果是登录页面,不执行

  if (history.location.pathname !== loginPath) {
    const currentUser = await fetchUserInfo();
    return {
      fetchUserInfo,
      currentUser,
      settings: {},
    };
  }

  return {
    fetchUserInfo,
    settings: {},
  };
} // ProLayout 支持的api https://procomponents.ant.design/components/layout

const LeaveModal = (props) => {
  const [leaveModalVisible, changeLeaveModalVisible] = useState(false);

  useEffect(() => {
    changeLeaveModalVisible(props?.leaveModalVisible);
  }, [props?.leaveModalVisible]);

  const { newTabList, changeTab } = useModel('tabList', (ret) => ({
    changeTab: ret.changeTab,
    newTabList: ret.newTabList,
  }));

  const { changeDetailStatus } = useModel('detailStatus', (ret) => ({
    changeDetailStatus: ret.changeDetailStatus,
  }));

  return (
    <Modal
      width={640}
      destroyOnClose
      title={'离开提示'}
      visible={leaveModalVisible}
      onCancel={() => {
        changeLeaveModalVisible(false);
      }}
      onOk={async () => {
        changeLeaveModalVisible(false);
        changeDetailStatus(false);
        history.push(`${props?.path}`);
        let tabListArray = [];
        tabListArray = await getTab(props?.path, newTabList);
        changeTab(tabListArray);
      }}
    >
      <div className={styles.modalView}>
        <QuestionCircleOutlined style={{ color: '#faad14', fontSize: 30, marginRight: 18 }} />
        <div>
          <p>离开此页面,系统不会保存所做修改,确认离开?</p>
        </div>
      </div>
    </Modal>
  );
};

export const layout = ({ initialState }) => {
  return {
    rightContentRender: () => <RightContent />,
    //点击子菜单的时候,其他菜单不自动收起来
    // openKeys: false,
    disableContentMargin: false,
    // waterMarkProps: {
    //   content: initialState?.currentUser?.name,
    // },
    onPageChange: async () => {
      const { location } = history; // 如果没有登录,重定向到 login
      // console.log('============onPageChange location', location);
      if (location.pathname === '/') {
        history.push('/404');
      }
      // if (!initialState?.currentUser && location.pathname !== loginPath) {
      //   history.push(loginPath);
      // }
    },

    menuDataRender: () => customMenuDate,

    //自定义menu
    menuItemRender: (item, dom) => {
      const [pathname, changePathname] = useState('');
      const [leaveModalVisible, changeLeaveModalVisible] = useState(false);

      const { detailStatus } = useModel('detailStatus', (ret) => ({
        detailStatus: ret.detailStatus,
      }));

      async function changeMenu() {
        const { location } = history;
        if (location.pathname.includes('/detail') && detailStatus) {
          changePathname(item.path);
          changeLeaveModalVisible(true);
        } else {
          history.push(item.path);
        }
      }
      return (
        <>
          <LeaveModal path={pathname} leaveModalVisible={leaveModalVisible} />
          <div onClick={() => changeMenu()}>{dom}</div>
        </>
      );
    },
    //自定义有子menu
    // subMenuItemRender: (item, dom) => {
    //   const [pathname, changePathname] = useState('');
    //   const [leaveModalVisible, changeLeaveModalVisible] = useState(false);

    //   const { detailStatus } = useModel('detailStatus', (ret) => ({
    //     detailStatus: ret.detailStatus,
    //   }));

    //   function changeMenu() {
    //     const { location } = history;
    //     if (location.pathname.includes('/detail') && detailStatus) {
    //       changePathname(item.path);
    //       changeLeaveModalVisible(true);
    //     } else {
    //       history.push(item.path);
    //     }
    //   }
    //   return (
    //     <>
    //       <LeaveModal path={pathname} leaveModalVisible={leaveModalVisible} />
    //       <div onClick={() => changeMenu()}>{dom}</div>
    //     </>
    //   );
    // },
    //自定义面包屑
    itemRender: (route, params, routes, paths) => {
      const [leaveModalVisible, changeLeaveModalVisible] = useState(false);

      const { detailStatus } = useModel('detailStatus', (ret) => ({
        detailStatus: ret.detailStatus,
      }));

      function changeBread() {
        const { location } = history;
        if (location.pathname.includes('/detail') && detailStatus) {
          changeLeaveModalVisible(true);
        } else {
          history.push(route.path);
        }
      }
      return (
        <>
          <LeaveModal path={route.path} leaveModalVisible={leaveModalVisible} />
          <span to={route.path} onClick={() => changeBread()} style={{ cursor: 'pointer' }}>
            {route.breadcrumbName}
          </span>
        </>
      );
    },
    menuHeaderRender: undefined,
    // 自定义 403 页面
    // unAccessible: <div>unAccessible</div>,
    // 增加一个 loading 的状态
    // childrenRender: (children) => {
    //   if (initialState.loading) return <PageLoading />;
    //   return children;
    // },
    childrenRender: (children) => {
      const { newTabList, changeTab } = useModel('tabList', (ret) => ({
        changeTab: ret.changeTab,
        newTabList: ret.newTabList,
      }));
      const { detailStatus, changeDetailStatus } = useModel('detailStatus', (ret) => ({
        detailStatus: ret.detailStatus,
        changeDetailStatus: ret.changeDetailStatus,
      }));

      const [activeKey, changeActiveKey] = useState('');
      const [newTabListData, changeNewTabList] = useState([]);
      const [leaveModalVisible, changeLeaveModalVisible] = useState(false);
      const [newActiveKey, changeNewActiveKey] = useState('');
      const [handleStatus, changeHandleStatus] = useState('');
      const { drop, dropScope, clear, getCachingNodes } = useAliveController();
      const { location } = history;

      useEffect(async () => {
        let tabListArray = [];
        if (location?.pathname.includes('/detail')) {
          changeActiveKey(location?.pathname.split('/detail')[0]);
          let detail = await findCompletePath(location?.pathname.split('/detail')[0]);
          tabListArray = await getTab(detail, newTabList);
        } else {
          let detail = await findCompletePath(location?.pathname);
          if (isEmpty(detail)) {
            tabListArray = await getTab({ key: '/404', tab: '欢迎', closable: false }, newTabList);
            changeActiveKey('/404');
          } else {
            tabListArray = await getTab(detail, newTabList);
            changeActiveKey(location?.pathname);
          }
        }
        changeNewTabList(tabListArray);
        changeTab(tabListArray);
      }, [location?.pathname]);

      function findCompletePath(passName) {
        for (let i = 0; i < customMenuDate?.length; i++) {
          for (let j = 0; j < customMenuDate[i].routes?.length; j++) {
            let detail = customMenuDate[i].routes[j];
            if (detail.path === passName) {
              return { tab: detail.name, key: detail.path };
            } else {
              for (let k = 0; k < detail.routes?.length; k++) {
                if (detail.routes[k].path === passName) {
                  return { tab: detail.routes[k].name, key: detail.routes[k].path };
                }
              }
            }
          }
        }
      }

      const handleTabChange = (key) => {
        if (location?.pathname.includes('/detail') && detailStatus) {
          changeLeaveModalVisible(true);
          changeNewActiveKey(key);
          changeHandleStatus('onChange');
        } else {
          changeActiveKey(key);
        }
      };

      const remove = (key) => {
        if (location?.pathname.includes('/detail') && detailStatus) {
          changeLeaveModalVisible(true);
          changeNewActiveKey(key);
          changeHandleStatus('remove');
        } else {
          removeEvent(key);
        }
      };

      const removeEvent = (key) => {
        dropScope(key);
        drop(key);
        let lastIndex;
        let activeKeyRemove = _.cloneDeep(activeKey);
        newTabList.forEach((pane, i) => {
          if (pane.key === key) {
            lastIndex = i - 1;
          }
        });
        const newTabListFilter = newTabList.filter((pane) => pane.key !== key);
        if (newTabListFilter.length && activeKeyRemove === key) {
          if (lastIndex >= 0) {
            activeKeyRemove = newTabListFilter[lastIndex].key;
          } else {
            activeKeyRemove = newTabListFilter[0].key;
          }
        }
        changeTab(newTabListFilter);
        changeNewTabList(newTabListFilter);
        changeActiveKey(activeKeyRemove);
      };

      useEffect(() => {
        if (activeKey) {
          history.push(`${activeKey}`);
        }
      }, [activeKey]);

      return (
        <div className={styles.tabsContainer}>
          {location?.pathname.includes('/detail') ? (
            <PageContainer
              title={false}
              tabList={newTabListData}
              tabProps={{
                type: 'editable-card',
                hideAdd: true,
                onEdit: (e, action) => {
                  if (action === 'remove') {
                    remove(e);
                  }
                },
              }}
              tabActiveKey={activeKey}
              onTabChange={handleTabChange}
            >
              {children}
              {/* {newTabListData?.length > 0 ? <Detail /> : null} */}
            </PageContainer>
          ) : (
            <PageContainer
              breadcrumb={'none'}
              title={false}
              tabList={newTabListData}
              tabProps={{
                type: 'editable-card',
                hideAdd: true,
                onEdit: (e, action) => {
                  if (action === 'remove') {
                    remove(e);
                  }
                },
              }}
              tabActiveKey={activeKey}
              onTabChange={handleTabChange}
            >
              {/* KeepAlive必须加上name和id,否则切换menu,页面不会变化 */}
              {/* {newTabListData?.length > 0 ? props.children : null} */}
              {newTabList?.length > 0 ? (
                <KeepAlive
                  when={true}
                  name={location?.pathname}
                  id={location?.pathname}
                  saveScrollPosition="screen"
                >
                  {children}
                </KeepAlive>
              ) : null}
            </PageContainer>
          )}

          <Modal
            width={640}
            destroyOnClose
            maskClosable={false}
            title={'离开提示'}
            visible={leaveModalVisible}
            onCancel={() => {
              changeLeaveModalVisible(false);
            }}
            onOk={() => {
              changeLeaveModalVisible(false);
              changeDetailStatus(false);
              if (handleStatus === 'onChange') {
                changeActiveKey(newActiveKey);
              } else if (handleStatus === 'remove') {
                removeEvent(newActiveKey);
              }
            }}
          >
            <div className={styles.modalView}>
              <QuestionCircleOutlined style={{ color: '#faad14', fontSize: 30, marginRight: 18 }} />
              <div>
                <p>离开此页面,系统不会保存所做修改,确认离开?</p>
              </div>
            </div>
          </Modal>
        </div>
      );
    },
    ...initialState?.settings,
  };
};

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐