头部面包屑改成多页签
跟着vue3.x + ts + vue-router + pinia简易多页签方案实现一个头部面包屑改成多页签路由缓存功能。
由于菜单存在一级菜单和二级菜单,使用缓存时一定要给动态组件一个key值,否则路由均嵌套在一个页面内。
此时还有另外一个问题,在keep-alive外层过渡动画transition,此时的动画对于二级菜单是无效的,并且过度动画里需要对元素有一个创建和销毁的过程动画才能生效。
而且keep-alive内部只能缓存第一级根元素,如果用transition包裹动态组件,缓存动态路由组件就会失效了。
故此时不使用过度动画了,但缓存好了,如何关闭呢?如何清除缓存呢。此时可能想到keep-alive的include
但项目写好的页面已经存在很多使用setup语法糖script标签未命名的页面,不可能将所有页面重构成非setup语法糖的写法。
我在vue3的issue里找到加script标签的解决方案,但是还是不想给每个页面添加也个新的名称。
故给缓存的动态组件绑定路由的完整路径fullName,并且这个key末尾再加一个随机数,这样路由标签就可以关闭了。
其原理是其实缓存组件并没有销毁掉,只是处于失活状态,在重新打开路由时,创建了一个新的key值,修改的是随机数,故重新创建了一个动态路由组件进行渲染,从而达到刷新的目的。
但缓存的组件越来越多怎么办?此时max属性非常重要了,其缓存超过最大数值时,会自动销毁最前创建的缓存。
<router-view v-slot="{ Component,route}">
<keep-alive :max="20">
<component :is="Component"
:key="isNeedKeep[route.fullPath]"></component>
</keep-alive>
</router-view>
<script setup lang="ts">
const isNeedKeep = reactive({});
watch(
() => tabs,
() => {
const tabsFullpath = tabs.map((i) => i.fullPath);
Object.keys(isNeedKeep).forEach((i) => {
if (!tabsFullpath.includes(i)) {
const ran = Math.random().toFixed(5);
isNeedKeep[i] = `${isNeedKeep[i]}${ran}`;
}
});
},
{ deep: true }
);
onBeforeMount(() => {
const currentPath = route.fullPath;
if (!isNeedKeep[currentPath]) {
isNeedKeep[currentPath] = `${currentPath}_`;
}
});
</script>
上述方案存在隐藏问题,因此还是修改为keep-alive动态include方式
// route.ts 通过平台返回的菜单,配置一个组件名称
meta: {
cName: item.component?.substring(item.component.lastIndexOf('/') + 1)
}
<router-view v-slot="{ Component}">
<keep-alive :include="isNeedKeep">
<component :is="Component"></component>
</keep-alive>
</router-view>
<script lang="ts">
export default {
name: 'KeepIndex'
};
</script>
const NO_KEEP_NAME = ['HomePage'];
const navbarKeep = getStorage('NavbarKeep');
state: (): IState => {
return {
isNeedKeep: Array.isArray(navbarKeep) && navbarKeep.length > 0 ? (getStorage('NavbarKeep') as any) : ['KeepIndex']
};
actions: {
addTab(route: RouteLocationNormalizedLoaded) {
...
if (this.tabs.length >= 20) {
this.removeTab(1);
return;
}
...
if (!NO_KEEP_NAME.includes(cName)) {
this.isNeedKeep.push(cName);
}
...
setStorage('NavbarKeep', this.isNeedKeep);
},
removeTab(index: number) {
...
const keepIndex = this.isNeedKeep.findIndex((cn: string) => cn === reTab[0].cName);
if (keepIndex > -1) {
this.isNeedKeep.splice(keepIndex, 1);
setStorage('NavbarKeep', this.isNeedKeep);
}
}
}
});