react中使用keepAlive實現導航tabs

在後臺項目中使用tabs導航是一個重要的功能node

下面介紹一下若是配合umi框架和對應的插件實現此功能react

參考:react-activationgit

參考實現:實現一個tabsgithub

目前項目用的架構設計是umi3+react17+antd pro5npm

1.引入使用相關插件

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

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

2.公共組件封裝

components文件夾下建立KeepAvlieTabs
對應的less樣式請自行修改本身的需求antd

相關的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文件app

// /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 = () => {
  useActivate(()=>{
    console.log("我被激活了");
  })
  useUnactivate(()=>{
    console.log("我被緩存了");
  })
  return <div>我是被緩存的組件</div>;
};

export default (props: any) => {
  // 下面使用這樣的方式去控制路由操做
  // 是爲了同步顯示路由定義中的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 />,
    ...
  };
};
相關文章
相關標籤/搜索