---
title: "Tab 一闪，接口打两份工？我让 AI 当嘴替对齐 KeepAlive，把自己从生命周期鬼打墙里拽出来"
date: 2025-07-22
description: "线上 Tab 一切换，同一接口像复读机连打两遍——前端嫌烦，后端嫌吵。这篇不写复盘八股，只记我怎么把 Network、最小复现和几段模板丢给模型排假设，自己留着断点盖章；顺带弄清 KeepAlive 和动态 component :is 到底谁在演谁，最后用 v-show + 业务 key 收口，少听一半生命周期鬼故事。"
canonical_url: "https://yijinlee.com/articles/article-9"
tags:
  - "Vue 3"
  - KeepAlive
  - 动态组件
  - v-show
  - 生命周期
  - 人机协作排查
topic: "AI 外骨骼"
---

上午十点，群里的 @全体成员 比咖啡醒脑：“切个 Tab，接口怎么齐步走两次？卡得像喉咙里有痰。” 我瞄了眼 Network，两条请求并排躺着，姿势整齐，像我刚才心里默念的 “KeepAlive 应该很懂事啊” 被人当场打脸。

那几天没少跟框架斗心眼，我干脆把告警原文、父组件两段模板、文档里 KeepAlive 关键词揉成一页备忘丢给常用模型：**别灌鸡汤，按优先级列可能路径，每条附上怎么证伪。** 十分钟拿回一张还算干净的拓扑——`:is`、缓存 key、竞态……像有人先把白板擦了，我只用往上写第一层。

它有边界：**不会替你在线上呼吸。** 训练里学的是 “下文怎么接才像人写的”；你再塞点 changelog，只是把胡扯空间收窄。**路线图可以快出，章还得自己盖。**

下面是跟同事、跟文档、跟两次源码对过眼之后的版本；上面那段顶多算开场锣鼓。

---

### 一、案发现场：双倍请求不是灵异事件

首页两个 Tab：“活动 Events” 和 “附近 Nearby”。初代写法很克制，也很天真：

```html
<!-- 父组件容器 -->
<KeepAlive>
  <component :is="activeTab === 'events' ? Events : Nearby" />
</KeepAlive>
```

我心想：包了 `KeepAlive`，实例怎么也得像长住客，顶多出门抽根烟。**结果一切 Tab，子组件跟第一天入职似的重新 onMounted。** 请求写在这里？来都来了，再薅一把。

那一刻我才听懂用户说的 “卡”：**不一定慢在动画，可能慢在接口带薪加班。**

---

### 二、把锅扶稳：缓存到底在缓存谁

这部分不装读过十万行 runtime，只写我和文档对齐之后的结论：

**换的是组件类型，不是 “幕布后拎人”。** A、B 两个定义轮流上，卸载再挂载是默认套餐，不等于先把实例藏屏风后面。

**KeepAlive 也挑活。** 不是冻龄精华抹一脸就完事；类型交替上场，语义更接近 “换剧本”，生命周期该排的队还会排。

**onMounted 是扩音器。** 钩子多跑几次，它就老实多干几次活——**不读空气，只读调用。**

指望 “KeepAlive + 不同类型 `:is`” 自动替你省心，多半属于 **健身卡式自我感动**：卡办了，泳圈也可能涨了。

---

### 三、改法不玄学：v-show + 业务 key

就一句：**Tab 只是躲猫猫，用样式糊弄；真要换世界观，再上 key。**

#### 1. 父组件：握紧遥控器

```html
<!-- index.vue -->
<script setup>
const refreshKey = ref(0); // “业务户口本”版本号，别没事加一
const homeStore = useHomeStore();

watch(() => homeStore.getSelectedRegionId(), (newId, oldId) => {
  if (newId && oldId && newId !== oldId) {
    // 换城市这种硬变更，才配叫推倒重来
    refreshKey.value++;
  }
});
</script>

<template>
  <!-- v-show：人还在工位，只是暂时 unseen -->
  <Events v-show="activeTab === 'events'" :key="`events-${refreshKey}`" />
  <Nearby v-show="activeTab === 'nearby'" :key="`nearby-${refreshKey}`" />
</template>
```

#### 2. 子组件：别当全频段收音鸡

**能挪出 mounted 的拉数，就往 props 或明确的刷新信号那儿挪。**

**Store 监听漫天飞的，调试时群里艾特它比艾特谁都快——让它做回安静同事比较积德。**

---

### 四、收工前的几句人话

**KeepAlive 不是万金油。** 列表进详情那种深度往返它很香；同级 Tab 高频互瞟，多半是 v-show 更老实。

**`onMounted` 像进场，`onActivated` 像又回台前**（真跟 KeepAlive 过日子的时候别念混）。念混了，控制台里接口能凑一桌麻将。

**模型能提速，不能替你签字。** 假设挨个撞证据，比撞上 “感觉对了” 更有用。

收工说句俗的：重复请求少了，告警群安静得我反而不踏实。**框架有脾气，模型很会猜脾气，线上背锅的是你。** 不押韵，够跟下一个通宵的自己碰个杯。

## 参考文章：相关链接

- [Vue 3 · KeepAlive](https://vuejs.org/guide/built-ins/keep-alive.html)（官方文档）— activated/deactivated 与缓存实例语义，解释 Tab 闪一下打两份工。
- [Vue 3 · Lifecycle hooks](https://vuejs.org/guide/essentials/lifecycle.html)（权威引文）— 生命周期与副作用清理对照表，避免重复请求鬼打墙。
- [从 30 秒白屏到“秒开”：这次救命，一半是代码，一半是 AI](https://yijinlee.com/articles/article-11)（站内深度）— 同类「感知性能」排障，补齐加载侧证据。
- [谁把我的 Toast 偷走了？一次价值 300 万 Token 的"幽灵 Bug"捕获实录](https://yijinlee.com/articles/article-52)（站内深度）— 状态被「吃掉」的另一类幽灵现场。

