skip to content
usubeni fantasy logo Usubeni Fantasy

考虑路由来源的 Vue 页面缓存逻辑

/ 5 min read

前面还踩了些坑,但没有太多参考价值,就跳过了。

初版

为了实现从特定页面进入另一个特定页面时才需要缓存(例如列表到详情页),我们需要一个 map 确定它们的关系。除此之外应该没有别的方法,如果只使用路由栈缓存之前页面的话,没有办法分清楚流动方向。

<router-view v-slot="{ Component }">
<keep-alive :include="include">
<component :is="Component" />
</keep-alive>
</router-view>

模板部分如上十分简单,JavaScript 部分比较绕:

const shouldKeepAlive = [
'ServiceList',
'SinglePipelineView',
'ProcessorList',
'ApplicantList',
'PipelineTemplateList'
]
const include = ref(shouldKeepAlive) // 默认是所有需要缓存的页面
// 特定页面回来才需要缓存
// 目标页面: [来源页面]
const map: Record<string, string[]> = {
ServiceDetailView: ['ServiceList'],
SinglePipelineDetail: ['SinglePipelineView'],
SinglePipelineDesign: ['SinglePipelineView'],
TicketDetailView: ['ProcessorList', 'ApplicantList'],
ViewPipelineTemplateView: ['PipelineTemplateList'],
RouteRule: ['ServiceDetailView']
}
router.beforeResolve((to, from, next) => {
const toName = to.name as string
const currentShouldKeepAlive = map[toName]
if (shouldKeepAlive.includes(toName)) {
// 目标页面需要缓存时就设置到 include
// 这么做的问题是如果页面夹在中间,那么再往后跳会把前面的缓存覆盖,这也正是要优化算法的直接原因
include.value = [toName]
} else if (Object.keys(map).includes(toName)) {
// 如果不是需要缓存的页面,但是 需要缓存 的页面 的来源页面
// 那么保留来源页面
include.value = currentShouldKeepAlive
} else {
// 否则清除缓存
include.value = []
}
next()
})

逻辑都写在注释上了,如果看不懂的话,只能说很正常,自己写出来的时候就觉得很烂了,必定有更好的方案。

这堆代码的问题是 include 设置的条件过多,然后我发现其实 include 只要在页面离开前设置就能生效……

换句话说,导致上面复杂逻辑的原因就是:我一开始误以为缓存必须在进入被缓存页面的时候设置,但其实只要在离开页面之前(也就是 next() 之前),页面信息未被销毁,这个时候加入 include 也能成功缓存到,这么一来思路又解放了。

P.S. 注意这里有一个新手常犯的错误,keep-alive 用的 name组件的名称并非路由的名称,因此比较方便的方法是路由与组件同名。

新方案

const include = ref<string[]>([])
// 特定页面回来才需要缓存
// 目标页面: [来源页面]
const map: Record<string, string[]> = {
ServiceDetailView: ['ServiceList'],
SinglePipelineDetail: ['SinglePipelineView'],
SinglePipelineDesign: ['SinglePipelineView'],
TicketDetailView: ['ProcessorList', 'ApplicantList'],
ViewPipelineTemplateView: ['PipelineTemplateList'],
RouteRule: ['ServiceList', 'ServiceDetailView'] // ServiceList 是深层
}
router.beforeResolve((to, _, next) => {
const toName = to.name as string
include.value = [...(map[toName] || []), toName]
next()
})

思路解放后,不再拘泥初次进页面的动作,shouldKeepAlive 就不需要了,我们只需要在进入新页面之前,检测当前页面需不需要缓存就好了。

所以 include.value = map[toName] 是最基本的操作,不过如果只是这么写的话,真正进入需要缓存的页面时,include 又会被清空,那就又不成立了。

为此需要再加上目标自己,include.value = [...(map[toName] || []), toName] 这样就(应该)解决所有问题了,并且支持深层返回。

🥳 很好!一个 if 都不需要!核心就是构建目标页面和来源页面的关系,然后在离开前更新 include

当然这个也不是唯一解,各位大佬们可以积极开发出更好理解的方法😏

评论组件加载中……