前言
在开发影视聚合站时,我们遇到了 Vue 3 KeepAlive 在多组件缓存场景下的严重 Bug——一旦多个组件同时被 KeepAlive 缓存,页面间的导航就会完全失效。本文将记录诊断过程和解决方案。
问题背景
我们新增了4个影视推荐页面(电影/剧集/综艺/动漫),希望它们和原有的「源站」页面一样,在用户跳转到播放页再返回时保留滚动位置和已加载的数据。
原方案很简单:使用 Vue 3 的 <KeepAlive> 配合 include 属性将需要缓存的组件名加入列表。当将新页面加入 include 列表后,导航完全失效——URL变了,页面不变。
根因分析
Vue 3 KeepAlive 在多组件缓存场景下不可靠。 当 KeepAlive 内部切换多个已被缓存的组件类型时,缓存匹配逻辑会出错,导致 DOM 不更新。只缓存一个组件时正常工作,include 列表中有多个不同组件时导航就坏掉。
最终方案:混合缓存策略
1. KeepAlive 只缓存源站
源站是项目中最复杂的页面之一,原有 KeepAlive 行为保留不动:
<keep-alive :include="['SourceSiteView']">
<component :is="Component" />
</keep-alive>
2. 手动缓存:模块级变量 + 生命周期钩子
由于 Vue 3 的 <script setup> 中的所有顶级代码都会被编译进 setup() 函数体内,每次创建组件实例都会重新执行,所以缓存在 <script setup> 中声明是不生效的。
正确做法:用单独的非 <script setup> 块声明模块级变量:
<script lang="ts">
const pageCache: Record<string, PageCache> = {};
</script>
<script setup lang="ts">
// 离开时保存状态
onBeforeUnmount(() => {
pageCache[key] = {
items: items.value,
scrollY: window.scrollY,
// ...其他状态
};
});
// 重新进入时恢复
onMounted(() => {
const cached = pageCache[key];
if (cached) {
items.value = cached.items;
nextTick(() => window.scrollTo(0, cached.scrollY));
return; // 跳过 API 请求
}
load(); // 首次加载
});
</script>
3. 搜索页的 immediate watcher 处理
搜索页有 watch({ immediate: true }),缓存恢复时需要在 watcher 中提前返回避免重复搜索。
总结
- KeepAlive 多组件缓存有 Bug,单组件没问题
- <script setup> 中的变量是实例级,跨实例共享需用单独的 <script> 块
- 手动缓存是可靠替代,onBeforeUnmount 保存 + onMounted 恢复
- 混合策略:已有 KeepAlive 单组件不动,新页面用手动缓存
讨论
还没有留言,来留下第一条评论吧!