乐趣区

关于react.js:react中使用keepAlive实现导航tabs

在后盾我的项目中应用 tabs 导航是一个重要的性能

上面介绍一下如果配合 umi 框架和对应的插件实现此性能

参考:react-activation

参考实现:实现一个 tabs

目前我的项目用的架构设计是 umi3+react17+antd pro5

1. 引入应用相干插件

插件地址: umi-plugin-keep-alive

npm install umi-plugin-keep-alive --save
# or
yarn add umi-plugin-keep-alive

2. 公共组件封装
components 文件夹下创立 KeepAvlieTabs
对应的 less 款式请自行批改本人的需要

相干的 keepAlive 与生命周期应用计划及问题请到上述链接 react-activation 查看

创立 index.tsx 文件

// /components/KeepAvlieTabs/index.tsx
import {useAliveController} from 'react-activation';
import {arrayDeduplicate} from '@/utils/Array';
import type {CachingNode} from './type';
import Tab from './Tab';
import styles from './index.less';
import {useHistory, useLocation} from 'umi';

export default function KeepAliveTabs() {
  // history 导航
  const history = useHistory();
  // 本地路由信息
  const location = useLocation();
  // 获取缓存节点办法和信息
  const {getCachingNodes} = useAliveController();
  const cachingNodes: CachingNode[] = getCachingNodes();
  // 因为是异步组件,须要在这儿解决一下缓存中的反复问题
  let nodes: CachingNode[] = arrayDeduplicate(cachingNodes, 'path');
  // 首页不参加 tabs 切换的敞开操作
  nodes = nodes.filter((item) => item.path !== '/home');
  return (<ul className={styles['alive-tabs']}>
      <li
        className={location.pathname === '/home' ? styles.home_active : styles.home_deactive}
        onClick={() => {history.push('/home');
        }}
      >
        <div className="tags-nav">
          <span> 首页 </span>
        </div>
      </li>
      {nodes.map((node) => (<Tab key={node!.id} node={node} />
      ))}
    </ul>
  );
}

创立 Tab.tsx 文件

// /components/KeepAvlieTabs/Tab.tsx
import {useHistory, useLocation} from 'umi';
import {useAliveController} from 'react-activation';
import {CloseCircleOutlined} from '@ant-design/icons';
import type {CachingNode} from './type';

import styles from './index.less';

export default function Tab({node}: {node: CachingNode}) {const history = useHistory();
  const location = useLocation();
  // 同上,dropScope 是开释节点,点删除后删掉以后节点信息
  const {getCachingNodes, dropScope} = useAliveController();
  const cachingNodes: CachingNode[] | any[] = getCachingNodes();
  // 执行 tab 的删除操作
  function dropTab(e: React.MouseEvent<HTMLButtonElement>) {e.stopPropagation();
    // 如果敞开激活中的 KeepAlive Tab,须要先来到以后路由
    // 触发 KeepAlive unactivated 后再进行 drop
    if (location.pathname === node.path) {
      // 路由异步加载管制
      const unlisten = history.listen(() => {setTimeout(() => {dropScope(node.name as string | RegExp);
        }, 30);
      });
      unlisten();
      // 返回排除以后 node 后的最初一个 tab
      if (cachingNodes.length <= 1) {history.push('/');
      } else {const { path} = cachingNodes.filter((item) => item.path !== node.path).pop();
        history.push(path);
      }
    } else {dropScope(node.name as string | RegExp);
    }
  }
  // 设置以后 tab 的款式
  const className = () => {if (location.pathname === node.path) {if (location.pathname === '/home') {return `${styles.active}  ${styles.home_active}`;
      }
      return `${styles.active}`;
    }
    return `${styles.deactive}`;
  };
  
  return (
    <li
      className={className()}
      onClick={() => {history.push(node.path);
      }}
    >
      <div className="tags-nav">
        <span>{node.name}</span>
        {<CloseCircleOutlined className={styles['close-btn']} onClick={dropTab} />}
      </div>
    </li>
  );
}

创立类型文件 type.ts

export type CachingNode = {
  createTime: number;
  updateTime: number;
  name?: string;
  id: string;
  [key: string]: any;
};

创立款式 less 文件

.alive-tabs {
  height: 100%;
  margin: 0;
  padding: 0;
  overflow: hidden;
  white-space: nowrap;
  background: #fff;

  li {
    position: relative;
    display: inline-block;
    padding: 0 28px 0 10px;
    line-height: 32px;
    vertical-align: top;
    list-style: none;
    border-right: solid 1px #e6e6e6;
    cursor: pointer;
    transition: background 0.2s;
  }

  .active {
    height: 32px;
    background: rgba(#1890ff, 0.1);
    border-bottom: 2px solid #1890ff;
    // background-color: #42b983;
  }

  .home_active {
    height: 32px;
    padding: 0 10px;
    background: rgba(#1890ff, 0.1);
    border-bottom: 2px solid #1890ff;
    // background-color: #42b983;
  }

  .home_deactive {padding: 0 10px;}

  .deactive {background: #fff;}

  .close-btn {
    position: absolute;
    top: 11px;
    right: 10px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 12px;
    line-height: 0;
    outline: none;
    // transform: translateY(-50%);
  }
}

3. 在组件中应用 keepAlive

创立 Books/index.tsx

import React from 'react';
import {KeepAlive, useActivate, useUnactivate} from 'react-activation';

const Books: React.FC = () => {return <div> 我是被缓存的组件 </div>;};

export default (props: any) => {useActivate(()=>{console.log("我被激活了");
  })
  useUnactivate(()=>{console.log("我被缓存了");
  })
  // 上面应用这样的形式去管制路由操作
  // 是为了同步显示路由定义中的 title 和匹配路由 path 做对应的操作
  // 可依据自行需要批改以后的逻辑
  return (<KeepAlive name={props.route.name} path={props.route.path} saveScrollPosition="screen">
      <Books />
    </KeepAlive>
  );
};

4.keepAliveTabs 挂载到界面上
咱们的页面布局左高低构造,在 app.tsx 中,挂载到咱们的头部中,超出显示滚动条,具体的款式和性能请自行添加
未实现鼠标右键菜单敞开所有,左右切换,请自行添加,antd 有相干的组件

export const layout: RunTimeLayoutConfig = ({initialState}: any) => {
  return {
    // 管制组件是否可缓存
    disableMobile: true,
    headerContentRender: () => <KeepAliveTabs />,
    ...
  };
};
退出移动版