前言
上一篇写了 关闭当前标签 的功能实现,其中涉及了很多之前写过的知识点。今天就来看看关闭其他标签,关闭全部标签两个标签功能的实现,是否和关闭当前标签有什么关联。
case 'closeOther':
closeOtherTab(menu)
break
case 'closeAll':
closeAllTab(menu)
break
关闭其他标签
BuildAdmin中定义了closeOtherTab,用来关闭其他标签。在之前tabs的实现中曾经讲到,讲过tabs的渲染是通过v-for遍历tabsViews实现的。
那么关闭其他标签的实现思路就是:遍历navTabs中的tabsViews,通过filter只留下选中tab对应的menu路由即可 。这里我们先看closeOtherTab是如何定义的。
const closeOtherTab = (menu: RouteLocationNormalized) => {
navTabs.closeTabs(menu)
if (navTabs.state.activeRoute?.path !== menu.path) {
router.push(menu)
}
navTabs.setActiveRoute(menu)
}
closeTabs
首先调用了navTabs状态中closeTabs方法,我们去navTabs.ts去看看代码逻辑。
之前关闭按钮和弹出框关闭tab的两种方式,都是调用了closeTab,在实现关闭其他标签页时,重新定义了closeTabs方法。
const closeTabs = (retainMenu: RouteLocationNormalized | false = false) => {
if (retainMenu) {
state.tabsView = [retainMenu]
} else {
state.tabsView = []
}
}
参数retainMenu是需要保留的标签或者false。当传入一个menu时,tabsView会被初始化为只包含此menu的list,即关闭了其他所有的标签。当传入的是false,tabsView被初始化为空list,即关闭了所有的标签。
所以,closeTabs方法同时实现了关闭其他、关闭所有标签的两个功能,接着回到closeOtherTab,看看后面的实现。
滑动块
在关闭其他tab有两种情况:
- 关闭的tab是当前激活的,即滑动块所在的,路由不变。
- 关闭的tab是非激活的。
第一种情况,我们只需要考虑滑动块的位置改变即可。第二种情况,除了考虑滑动块位置的改变,还要跳转到选中tab的那个页面,即路由跳转。那么,滑动块的位置是如何改变的?
在之前滑动块的滑动实现中,我们定义了selectNavTab方法,当activeRoute改变时,就会调用此方法.
所以我们只需要修改activeRoute即可。对于tab是否为激活的判断,只需要将activeRoute和menu对比即可。所以closeOtherTab最后几行代码就实现了上面的两个功能点.
if (navTabs.state.activeRoute?.path !== menu.path) {
router.push(menu)
}
navTabs.setActiveRoute(menu)
这样,就实现了关闭其他标签。
关闭所有标签
关闭所有标签的设计思路为:关闭tabs栏中所有的tab,然后打开应用的第一个tab(即firstRoute,控制台)。所以,这里也会出现两种情况:
- 在控制台的tab上,选择关闭所有标签。
- 在非控制台的tab上,选择选择关闭所有标签。
第一种情况,其实直接关闭除了控制台之外的其他标签就行了,没有必要关闭了所有tab之后再创建一个控制台的tab,所以这种情况下问题就转变成了在控制台tab上关闭其他标签。
第二种情况,就是之前提及的设计思路。在BuildAdmin中,定义了closeAllTab方法来关闭所有标签。
const closeAllTab = (menu: RouteLocationNormalized) => {
const firstRoute = getFirstRoute(navTabs.state.tabsViewRoutes)
// 第一种情况
if (firstRoute && firstRoute.path == menu.path) {
return closeOtherTab(menu)
}
// 第二种情况
navTabs.closeTabs(false)
if (firstRoute) router.push(firstRoute.path)
}
首先获取firstRoute来判断当前激活的tab是不是控制台,如果是,直接调用closeOtherTab关闭除控制台之外的tab。如果不是,则调用navTabs.closeTabs并传入false。
在上面关闭其他标签中讲过,传入false,tabsView被初始化为空的list。意味着tabs栏中将没有tab,这时候路由再跳转到firstRoute,打开控制台的tab。这样就实现了关闭所有标签的功能。
优化
虽然实现了关闭其他标签、关闭所有标签的功能,但是在后面的使用中可以根据个人的需要进行优化。
关闭其他标签
如果我们在某些页面上做了一些修改,当使用关闭其他标签关闭了这些页面之后,再重新打开,你会发现这些修改了的数据还是存在的。
如图,新建的控制台页面count为0,我将count累加到6之后,通过关闭其他标签关闭了控制台,重新打开控制台的count还是7。关闭所有标签同样也面临这个问题。
关闭所有标签
BuildAdmin中,控制台是firstRoute。所以当我们关闭所有标签时,会在tabs栏自动创建渲染控制台的tab。
在关闭所有tab时,如果tabs中没有控制台,则会新建控制台tab;如果tabs中有控制台,看起来是关闭了所有之后再新建控制台,其实还是复用了之前的组件。
如图,新建的控制台页面count为0,我将count累加到7之后关闭所有标签,然后“新建”的控制台的count还是7。
方案
出现这种现象的原因,就是之前讲的keep-alive的缓存机制导致的,具体可以参考BuildAdmin13:重新加载和keep-alive缓存。虽然通过标签关闭了页面,但是keep-alive的keepAliveComponentNameList中的组件缓存还没有删除。
但是通过关闭按钮关闭的tab,再重打开之后缓存就没了。
是因为在closeTab中通过mitt时间总线库,定义onTabViewClose事件关闭了对应tab的缓存。
同样,通过弹出框的关闭页面标签也没有缓存。
是因为关闭页面标签直接复用了closeTab,所以关闭页面的同时也删除了缓存。
那么,对于关闭其他标签、关闭所有标签删除缓存,其实也很简单。分别定义一个mitt事件,关闭其他标签时,遍历keepAliveComponentNameList,根据tab的route(menu)只留下此tab对应的组件缓存。
关闭所有标签时更为简单,直接将keepAliveComponentNameList = [],然后对firstRoute新建,并将新建的组件放入缓存中。
可以参考BuildAdmin14:关闭tab的mitt实现 onTabViewClose事件的实现。这两个部分的具体实现我这里就不写了,因为BuildAdmin中也是默认不删除缓存。
勾选了选择框,通过关闭其他关闭之后再打开,复选框依旧被勾选。存在即合理,有些场景下还是需要这种缓存功能的。
结语
本篇文章实现了弹出框的关闭其他标签和关闭所有标签的功能,归根结底还是对之前各个模块功能实现的再次整合。所以,还是那句话,学习是一个层层递进的理解过程,学了后面的不要忘了前面的。
至此,弹出框的五个标签已实现其四,下篇文章就写剩下的最后一个标签全屏,同时作为第五期训练营的第21篇文章,来做一个good ending。