整体思路
1. 封装自定义tabbar组件 需要监听当前的路由路径,当路由发生变化的时候,需要存tabslist和 keepaliveName的数据
2. 封装存储和移除tabsList的数据的方法(pinia)用于显示页签
3. 封装缓存和移除的组件名字keepaliveName的方法(pinia)用于keepalive组件的include属性
4. 在路由出口的位置需要使用keepalive组件和component组件 缓存组件的数据 实现切换tab后,上一tab页的内容仍然保留
第一步 封装自定义tabbar组件
<div class="tabs-box">
<div class="tabs-menu">
<el-tabs
v-model="tabsMenuValue"
type="card"
@tab-click="tabClick"
@tab-remove="tabRemove"
>
<el-tab-pane
v-for="item in tabsMenuList"
:key="item.path"
:label="item.title"
:name="item.path"
:closable="item.close"
>
<template #label>
<el-icon class="tabs-icon" v-show="item.icon && tabsIcon"></el-icon>
{{ item.title }}
</template>
</el-tab-pane>
</el-tabs>
</div>
</div>
// Tab Click 点击每一个tab跳转到对应的页面
const tabClick = (tabItem: TabsPaneContext) => {
const fullPath = tabItem.props.name as string
console.log('fullPath', fullPath)
router.push(fullPath)
}
// Remove Tab 点击每一个tab上的icon移除当前tab
const tabRemove = (fullPath: TabPaneName) => {
//tab-remove方法 会返回一个回调函数 fullpath
console.log('fullPath', fullPath)
const name =
tabStore.tabsMenuList.filter((item: any) => item.path == fullPath)[0]
.name || ''
// 移除keepaliveName和tabsList的数据
keepAliveStore.removeKeepAliveName(name)
tabStore.removeTabs(fullPath as string, fullPath == route.fullPath)
}
// 监听路由的变化(防止浏览器后退/前进不变化 tabsMenuValue)
watch(
() => route.fullPath,
() => {
const routeList = route.fullPath.split('/')
const name = routeList[routeList.length - 1]
tabsMenuValue.value = route.fullPath
const tabsParams = {
icon: route.meta.icon as string,
title: route.meta.title as string,
path: route.fullPath,
name: name as string,
close: true,
}
// 当路由发生变化时 给tabsList 和 keepaliveName中添加数据
tabStore.addTabs(tabsParams)
keepAliveStore.addKeepAliveName(name)
},
// 一来进来页面就监听
{ immediate: true },
)
第二步 封装存储和移除tabsList的数据的方法
// addtab
async addTabs(tabItem: TabsMenuProps) {
//要往tabsList添加的tab 是否在list中,如果不在就添加
console.log('tabItem', tabItem)
const res1 = this.tabsMenuList.every((item) => {
console.log('item', item)
return item.path !== tabItem.path
})
console.log('res1', res1)
if (this.tabsMenuList.every((item) => item.path !== tabItem.path)) {
this.tabsMenuList.push(tabItem)
console.log('tabsMenuList', this.tabsMenuList)
}
},
// Remove Tabs
async removeTabs(tabPath: string, isCurrent: boolean = true) {
// isCurrent 判断当前的fullpath和路由获取到的fullpath 是否一致
// 1. 把当前tabs关闭
// 2. 显示上一个tabs内容
const tabsMenuList = this.tabsMenuList
if (isCurrent) {
tabsMenuList.forEach((item, index) => {
if (item.path !== tabPath) return
// ?什么情况下会 加1 显示上一个tabs内容
const nextTab = tabsMenuList[index + 1] || tabsMenuList[index - 1]
if (!nextTab) return
router.push(nextTab.path)
})
}
//把当前tabs关闭
this.tabsMenuList = tabsMenuList.filter((item) => item.path !== tabPath)
},
第三步 封装缓存和移除的组件名字keepaliveName的方法
// Add KeepAliveName
async addKeepAliveName(name: string) {
!this.keepAliveName.includes(name) && this.keepAliveName.push(name)
// console.log('keepAliveName3333333', this.keepAliveName)
},
// Remove KeepAliveName
async removeKeepAliveName(name: string) {
this.keepAliveName = this.keepAliveName.filter((item) => item !== name)
},
第四步 在路由出口的位置缓存数据
<router-view v-slot="{ Component }">
<!-- transition 过渡动画 -->
<transition name="fade">
<!-- 渲染layout一级路由组件的子路由 -->
<!--:key 每次切换时,通过修改 key 的值,可以避免缓存中的组件被复用,从而达到刷新组件的目的-->
<keep-alive :include="keepAliveName">
<component :is="Component" v-if="flag" :key="route.path" />
</keep-alive>
</transition>
</router-view>