跳转到主要内容

crayonxiaoxin

Vue 3 KeepAlive 多组件缓存踩坑记

前言

在开发影视聚合站时,我们遇到了 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 单组件不动,新页面用手动缓存

讨论

还没有留言,来留下第一条评论吧!

留下足迹