共计 4253 个字符,预计需要花费 11 分钟才能阅读完成。
在后盾我的项目中应用 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 />, | |
... | |
}; | |
}; |
正文完