Vue3中的keep-alive组件用于缓存页面,以便在切换页面时保留其状态,但是这个状态是永久的,如果需求是列表页进入详情页后,切换回列表页,需要对列表页进行缓存,如果从首页进入列表页,就要重新加载列表页,这样就涉及到要手动删除keep-alive的缓存了
但是,keep-alive 最大的难题就是缓存的清理,如果能有简单的缓存清理方法,那么keep-alive 组件用起来就很爽。
很可惜,keep-alive 组件没有提供清除缓存的API,那有没有其他清除缓存的办法呢?答案是有的。我们先看看 keep-alive 组件的props:
include - string | RegExp | Array。只有名称匹配的组件会被缓存。
exclude - string | RegExp | Array。任何名称匹配的组件都不会被缓存。
max - number | string。最多可以缓存多少组件实例。
从include描述来看,我发现include是可以用来清除缓存,做法是:将组件名称添加到include里,组件会被缓存;移除组件名称,组件缓存会被清除。根据这个原理,用hook简单封装一下代码:
import { ref, nextTick } from 'vue'
const caches = ref<string[]>([])
export default function useRouteCache () {
// 添加缓存的路由组件
function addCache (componentName: string | string []) {
if (Array.isArray(componentName)) {
componentName.forEach(addCache)
return
}
if (!componentName || caches.value.includes(componentName)) return
caches.value.push(componentName)
}
// 移除缓存的路由组件
function removeCache (componentName: string) {
const index = caches.value.indexOf(componentName)
if (index > -1) {
return caches.value.splice(index, 1)
}
}
// 清空路由
function clearCache () {
caches.value = [];
}
// 移除缓存的路由组件的实例
async function removeCacheEntry (componentName: string) {
if (removeCache(componentName)) {
await nextTick()
addCache(componentName)
}
}
return {
caches,
addCache,
removeCache,
clearCache,
removeCacheEntry
}
}
注意,addCache的参数是componentName,也就是组件名称,如果使用setup的风格,需要在自行定义组件名称,例如:
<script setup lang="ts">
defineOptions({
name: 'List',
})
</script>
hook的用法如下:
<template>
<RouterView v-slot="{ Component, route }">
<keep-alive :include="caches">
<component :is="Component" :key="route.path" v-if="$route.meta.keepAlive" />
</keep-alive>
<component :is="Component" :key="route.path" v-if="!$route.meta.keepAlive" />
</RouterView>
</template>
<script setup lang="ts">
import { RouterView } from 'vue-router'
import useRouteCache from '@/hooks/useRouteCache'
const { caches } = useRouteCache()
</script>
添加和清除缓存的时机,原文章写的有点死,我的想法是这样的:
在路由守护里进行统一管理,代码如下:
import useRouteCache from '@/hooks/useRouteCache'
const { addCache, removeCache, clearCache } = useRouteCache()
// 路由前置守卫
router.beforeEach((to, from, next) => {
const { meta, name } = to;
const { keepAlive, componentName, routeLevel } = meta;
// 取出组件名称
const _componentName = componentName as string;
if(keepAlive && _componentName){
addCache(_componentName);
}
// 返回首页的,直接清空缓存,应对直接跳转首页的场景
if(name === 'HomeView'){
clearCache();
}else{
// 取出页面等级
const _routeLevel = routeLevel as number;
removeCacheRoute(from, _routeLevel);
}
next();
});
// 删除缓存
const removeCacheRoute = (from : RouteLocationNormalized, toLevel: number) => {
const { meta } = from;
const { keepAlive, componentName, routeLevel } = meta;
const _componentName = componentName as string;
const _fromLevel = routeLevel as number;
if(keepAlive && _componentName && _fromLevel > toLevel){
removeCache(_componentName);
}
}
添加缓存的时机是根据keepAlive和组件名称来的,如果都为true,则缓存页面
清除缓存的时机是根据页面等级来的,比如:首页的等级=0,列表页=1,详细页=2,当from>to时,则认为是列表页回到了首页,则需要清除缓存,否则就是进入详细页,则不清除缓存!
路由配置文件如下:
export default [
{
path: '/',
name: 'Home',
meta: { title: '首页', keepAlive: false, routeLevel: 0 },
component: () => import('@/views/Home.vue')
},
{
path: '/list',
name: 'List',
meta: { title: '列表', keepAlive: true, routeLevel: 1, componentName: 'List' },
component: () => import('@/views/List.vue')
},
{
path: '/detail',
name: 'Detail',
meta: { title: '详细', keepAlive: false, routeLevel: 2 },
component: () => import('@/views/Detail.vue'),
},
]