前言
页面缓存是一种很常见的需求,特别是对于现代admin框架,不同菜单之间切换页面会有需求保持上一次页面的内容、操作状态等。为了实现这个需求我们可以使用官方内置的keep-alive组件,然后该组件只能通过组件的name去控制缓存,但是实际场景中一个页面会通过参数控制打开多次,这种情况下路由是一样的,只是参数值不同,此时keep-alive组件会出一些小问题。
目标
- 缓存可以通过自定义行为删除
- 相同路由的不同参数可以独立缓存,互不影响
方案
目前可以实现的方案有多种,这里罗列一些常见的。我们这里只讨论vue3,vue2不在范畴内。
魔改源码
改源码是最直接的方法,曾经我们的项目就直接改了keepalive的源码,把key也可以当做控制参数。这个改动倒是不算大,但是官方迟迟不改肯定是有原因的,而且现在针对这个需求issue也有不少。但是存在一个问题,源码修改会导致项目不方便升级依赖。 不推荐!
重新定义组件
vue3提供了defineComponent
方法,可以在运行时定义一个组件。利用这个我们可以在打开页面的时候给每个内嵌在router-view
组件进行重新定义,把组件的name重新设定。
推荐!
实现(伪代码)
全局代码
// 缓存
export const cached = reactive({})
export const includes = ref([])
watch(
() => cached,
() => {
requestIdleCallback(() => {
const tmp = []
for (let cachedKey in cached) {
if (cached[cachedKey].isKeepalive){
tmp.push(cachedKey)
}
}
includes.value = tmp
})
},{
deep:true
})
const currentMenuRoute = framework.getCurrentMenuRoute()
router.afterEach(to => {
if (!currentMenuRoute.value){
return
}
const isKeepalive = to.meta.isKeepalive || false
const pageId = currentMenuRoute.value?.pageId
if (cached[pageId]){
return
}
cached[pageId] = {
pageId,
isKeepalive,
component:null,
}
})
实现缓存组件
cache-alive.vue
<script>
import {h, KeepAlive, toRaw} from 'vue'
import {reDefineComponent,includes} from '@/global/router'
export default {
props: {
is: {
type: Object,
require: true
},
route: {
type: Object,
require: true
}
},
setup(props) {
return ()=>h(KeepAlive, {include:toRaw(includes.value)}, {default: () => reDefineComponent(props.is,props.route)})
},
}
</script>
具体使用
注释部分的代码是未改造前的用法。
<router-view v-slot="{ Component,route }">
<cache-alive :is="Component" :route="route"></cache-alive>
<!-- <keep-alive>-->
<!-- <component :is="Component" />
<!-- </keep-alive>-->
</router-view>
实例源码参考
- happyboot-tiger/index.js at 7ac3cbaef5c00d8a366570103162e3d055eb5146 · pumelotea/happyboot-tiger (github.com)
- happyboot-tiger/cache-alive.vue at 7ac3cbaef5c00d8a366570103162e3d055eb5146 · pumelotea/happyboot-tiger (github.com)