前言 ¶
之前基础篇我们已经讲解了keep-alive基础使用,本篇主要讲解架构中keep-alive的二级和三级路由页面自动缓存,tab缓存原理等
缓存状态存储 ¶
src/store/basic.js
typescript
export const useBasicStore = defineStore('basic', {
state: () => {
return {
//keep-alive
cachedViews: [], //用于二级路由页面缓存
cachedViewsDeep: [],//用于三级路由页面缓存
}
},
actions: {
/*二级路由缓存*/
addCachedView(view) {
this.$patch((state) => {
if (state.cachedViews.includes(view)) return
state.cachedViews.push(view)
})
},
delCachedView(view) {
this.$patch((state) => {
const index = state.cachedViews.indexOf(view)
index > -1 && state.cachedViews.splice(index, 1)
})
},
/*三级路由缓存*/
addCachedViewDeep(view) {
this.$patch((state) => {
if (state.cachedViewsDeep.includes(view)) return
state.cachedViewsDeep.push(view)
})
},
delCacheViewDeep(view) {
this.$patch((state) => {
const index = state.cachedViewsDeep.indexOf(view)
index > -1 && state.cachedViewsDeep.splice(index, 1)
})
}
}
})
如何使用 ¶
在meta里设置cachePage或者leaveRmCachePage,决定是否需要缓存和移除缓存 各种组合情况
javascript
cachePage: true, leaveRmCachePage: true ->页面进入时缓存,离开时移除缓存
cachePage: false 或者不写 ->页面不缓存,按正常的页面走
cachePage: true, leaveRmCachePage: false -> 页面进入时缓存,离开时不移除缓存。那么此页面缓存会一直存在,除非手动移除
注意: 每个需要缓存的组件需要设置组件名字 组件设置的名字要和路由的name相同,因为keepAlive缓存就是根据组件的名字缓存的
javascript
<!--
使用keep-alive
1.设置name(必须)
2.在路由配置处设置cachePage:即可缓存
-->
<script setup name="KeepAlive">
</script>
//路由的name
{
path: 'keep-alive',
component: () => import('@/views/example/keep-alive'),
name: 'KeepAlive',
}
keep-alive核心源码分析 ¶
二级路由缓存源码分析 ¶
src/layout/app-main/index.vue
javascript
<script setup>
import { useBasicStore } from '@/store/basic'
const appStore = useBasicStore()
const route = useRoute()
const settings = computed(() => {
return appStore.settings
})
const key = computed(() => route.path)
//cachedViews: Array<string> 存储页面name
const cachedViews = computed(() => {
return appStore.cachedViews
})
let oldRoute = {}
let deepOldRouter = null
//移除当前页下的children缓存
const removeDeepChildren = (deepOldRouter) => {
deepOldRouter.children?.forEach((fItem) => {
appStore.setCacheViewDeep(fItem.name)
})
}
// cachePage: true ->页面初始化后缓存本页面
// leaveRmCachePage: true -> 页面离开后或者关闭后, 移除本页面缓存
// leaveRmCachePage和cachePage来自于router里的配置,请看下面介绍
//注:
// appStore.cachedViews:控制二级路由缓存
// appStore.cachedViewsDeep:控制三级路由缓存
//代码原理:通过监听路由里的name。从而获取当前路由,根据路由配置信息里的cachePage和leaveRmCachePage决定是否需要缓存和移除缓存
watch(
() => route.name,
() => {
//获取几级路由,如:routerLevel === 2 二级路由
const routerLevel = route.matched.length
//二级路由处理
if (routerLevel === 2) {
/**判断路由离开页面时是否需要移除缓存**/
if (deepOldRouter?.name) {
//页面离开时,如果有cachePage=true和leaveRmCachePage=true,则移除当前页面缓存
if (deepOldRouter.meta?.leaveRmCachePage && deepOldRouter.meta?.cachePage) {
appStore.delCachedView(deepOldRouter.name)
//remove the deepOldRouter‘s children component
removeDeepChildren(deepOldRouter)
}
} else {
if (oldRoute?.name) {
//页面离开时,如果有cachePage=true和leaveRmCachePage=true,则移除当前页面缓存
if (oldRoute.meta?.leaveRmCachePage && oldRoute.meta?.cachePage) {
//移除缓存
appStore.delCachedView(oldRoute.name)
}
}
}
/**判断路由进入页面时是否需要添加缓存**/
if (route.name) {
//页面进入时如果有cachePage=true,则设置页面缓存
if (route.meta?.cachePage) {
appStore.addCachedView(route.name)
}
}
deepOldRouter = null
}
//三级路由处理
if (routerLevel === 3) {
//三级时存储当前路由对象的上一级
const parentRoute = route.matched[1]
/**判断路由离开页面时是否需要移除缓存**/
//deepOldRouter不为空,且deepOldRouter不是当前路由的父对象,则需要清除deepOldRouter缓存
//一般为三级路由跳转三级路由的情况
if (deepOldRouter?.name && deepOldRouter.name !== parentRoute.name) {
if (deepOldRouter.meta?.leaveRmCachePage && deepOldRouter.meta?.cachePage) {
appStore.delCachedView(deepOldRouter.name)
//remove the deepOldRouter‘s children component
removeDeepChildren(deepOldRouter)
}
} else {
//否则走正常两级路由处理流程
if (oldRoute?.name) {
if (oldRoute.meta?.leaveRmCachePage && oldRoute.meta?.cachePage) {
appStore.setCacheViewDeep(oldRoute.name)
}
}
}
/**判断路由进入页面时是否需要添加缓存**/
//取的是第二级的name
if (parentRoute.name && parentRoute.meta?.cachePage) {
deepOldRouter = parentRoute
appStore.addCachedViewDeep(deepOldRouter.name)
if (route.name) {
if (route.meta?.cachePage) {
//第三级路由的页面进行缓存,通过route.name
appStore.addCachedViewDeep(route.name)
}
}
}
}
//保存上一个路由信息(也就是当前离开页面的路由信息)
oldRoute = JSON.parse(JSON.stringify({ name: route.name, meta: route.meta }))
},
//首次进入页面监听就触发
{ immediate: true }
)
</script>
三级路由页面缓存源码分析 ¶
javascript
<script setup>
import { useAppStore } from '@/store/app'
const appStore = useAppStore()
const route = useRoute()
const settings = computed(() => {
return appStore.settings
})
const key = computed(() => route.path)
//cachedViews: Array<string> 存储页面name
const cachedViews = computed(() => {
return appStore.cachedViews
})
let oldRoute = {}
let deepOldRouter = null
//移除当前页下的children缓存
const removeDeepChildren = (deepOldRouter) => {
deepOldRouter.children?.forEach((fItem) => {
appStore.setCacheViewDeep(fItem.name)
})
}
// cachePage: true ->页面初始化后缓存本页面
// leaveRmCachePage: true -> 页面离开后或者关闭后, 移除本页面缓存
// leaveRmCachePage和cachePage来自于router里的配置,请看下面介绍
//注:
// appStore.cachedViews:控制二级路由缓存
// appStore.cachedViewsDeep:控制三级路由缓存
//代码原理:通过监听路由里的name。从而获取当前路由,根据路由配置信息里的cachePage和leaveRmCachePage决定是否需要缓存和移除缓存
watch(
() => route.name,
() => {
//获取几级路由,如:routerLevel === 2 二级路由
const routerLevel = route.matched.length
//三级路由处理
if (routerLevel === 3) {
//三级时存储当前路由对象的上一级
const parentRoute = route.matched[1]
/**判断路由离开页面时是否需要移除缓存**/
//deepOldRouter不为空,且deepOldRouter不是当前路由的父对象,则需要清除deepOldRouter缓存
//一般为三级路由跳转三级路由的情况
if (deepOldRouter?.name && deepOldRouter.name !== parentRoute.name) {
if (deepOldRouter.meta?.leaveRmCachePage && deepOldRouter.meta?.cachePage) {
appStore.delCachedView(deepOldRouter.name)
//remove the deepOldRouter‘s children component
removeDeepChildren(deepOldRouter)
}
} else {
//否则走正常两级路由处理流程
if (oldRoute?.name) {
if (oldRoute.meta?.leaveRmCachePage && oldRoute.meta?.cachePage) {
appStore.setCacheViewDeep(oldRoute.name)
}
}
}
/**判断路由进入页面时是否需要添加缓存**/
//取的是第二级的name
if (parentRoute.name && parentRoute.meta?.cachePage) {
deepOldRouter = parentRoute
appStore.addCachedViewDeep(deepOldRouter.name)
if (route.name) {
if (route.meta?.cachePage) {
//第三级路由的页面进行缓存,通过route.name
appStore.addCachedViewDeep(route.name)
}
}
}
}
//保存上一个路由信息(也就是当前离开页面的路由信息)
oldRoute = JSON.parse(JSON.stringify({ name: route.name, meta: route.meta }))
},
//首次进入页面监听就触发
{ immediate: true }
)
</script>
目前仅支持2级和3级路由之间的页面缓存,清除缓存
如果清楚缓存的页面中含有children页面缓存,children页面也会一起清除
缓存组源码分析 ¶
配置了缓存组,当前配置页面下的多级页面,会根据配置的信息进行缓存
src/layout/app-main/index.vue
typescript
//缓存组处理
//当前跳转页如果不在缓存组中,则整个组的页面进行移除
if (cacheGroup.length) {
if (!cacheGroup.includes(route.name)) {
cacheGroup.forEach((item) => {
basicStore.delCachedView(item)
})
}
}
//当路由中配置了 cacheGroup 则会根据配置的数组页面名进行缓存
if (route.meta?.cacheGroup) {
cacheGroup = route.meta?.cacheGroup || []
cacheGroup.forEach((fItem) => {
basicStore.addCachedView(fItem)
})
}
如何使用
//src/router/modules/basic-demo.js
typescript
{
meta: { title: 'KeepAlive Group', cacheGroup: ['KeepAliveGroup', 'SecondChild', 'ThirdChild'] }
}
通过在 meta中配置 cacheGroup
tab标签栏缓存 ¶
注:缓存和tab没有关联,和路由配置有关联 ¶
架构为什么要这样设置呢?
1.缓存和tab没有关联,更利于缓存的灵活配置。如:当我们在settings.js中设置showTagsView为false时,依然可以使用路由配置的cachePage或者leaveRmCachePage进行设置缓存,TagsView的显示和隐藏对缓存没有影响。
2.和路由配置有关联,更利于我们对缓存的使用。如,我们可以根据路由配置的cachePage或者leaveRmCachePage,实现进行页面是否缓存,和离开页面页面是否移除缓存的组合式选择。
那么如果我想实现之前tab打开时,页面缓存,tab关闭时,移除缓存的功能呢? ¶
在想实现此功能页面的路由上设置
javascript
//如果配置了cachePage: true 则当前页面进入后,进行缓存。 默认是false
//closeTabRmCache:true 则当前页离开后,页面会被移除缓存。默认是false
meta: { title: 'Tab KeepAlive', cachePage: true, closeTabRmCache: true }
cachePage: true, closeTabRmCache: ture -> 进入时缓存,关闭时移除缓存