[特殊字符] Vue 3 中 Keep-Alive 对生命周期的影响:深度解析

🔄 Vue 3 中 Keep-Alive 对生命周期的影响:深度解析

在 Vue 开发中,性能优化是一个永恒的话题。当我们使用 <keep-alive> 包裹组件时,组件会被缓存而不是销毁。这虽然提升了性能,但也改变了组件的生命周期行为。

很多开发者会遇到这样的困惑:

"为什么我切换标签页后,onMounted 不再执行了?"

"为什么我在 onUnmounted里清理定时器,结果定时器还在跑?"

本文将带你揭开 keep-alive 的神秘面纱,掌握新增的两个关键生命周期钩子:activateddeactivated

📂 目录

  1. [核心概念:什么是 Keep-Alive?](#核心概念:什么是 Keep-Alive?)
  2. [生命周期对比:普通组件 vs 缓存组件](#生命周期对比:普通组件 vs 缓存组件)
  3. [新增钩子:Activated & Deactivated](#新增钩子:Activated & Deactivated)
  4. 实战演示:代码与现象
  5. [⚠️ 常见坑点与最佳实践](#⚠️ 常见坑点与最佳实践)
  6. [💡 总结](#💡 总结)

1. 核心概念:什么是 Keep-Alive?

<keep-alive> 是 Vue 内置的一个抽象组件,它不会渲染成 DOM 元素,也不会出现在父组件链中。

它的作用:

  • 缓存不活动的组件实例,而不是销毁它们。
  • 当组件再次被切换回来时,直接从缓存中读取,跳过创建和挂载过程

典型场景:

  • 标签页切换(如:首页、列表页、详情页)。
  • 表单填写中途切换,希望保留用户输入的内容。
vue 复制代码
<template>
  <keep-alive>
    <component :is="currentComponent" />
  </keep-alive>
</template>

2. 生命周期对比:普通组件 vs 缓存组件

理解 keep-alive 的关键,在于对比首次进入切换离开再次进入这三个阶段的生命周期变化。

📊 生命周期流程图

阶段 普通组件 (无 Keep-Alive) 缓存组件 (有 Keep-Alive) 说明
首次渲染 beforeCreate -> created -> beforeMount-> mounted beforeCreate -> created -> beforeMount-> mounted -> activated 首次进入时,activated 会在 mounted 之后触发。
切换离开 beforeUnmount -> unmounted deactivated 关键点! 组件不会被销毁,而是触发 deactivated
再次进入 beforeCreate -> ... -> mounted (重新创建) activated 关键点! 直接从缓存恢复,只触发 activated不触发 mounted

🔑 核心结论

  • keep-alive 缓存的组件,onMountedonUnmounted 只会执行一次(分别在第一次创建和最终真正销毁时)。
  • 后续的切换,只会触发 onActivatedonDeactivated

3. 新增钩子:Activated & Deactivated

Vue 3 Composition API 中提供了两个新的生命周期钩子来处理缓存组件的状态:

onActivated

  • 触发时机:组件被从缓存中激活(显示)时。
  • 用途
    • 重新获取最新数据(如果数据需要实时性)。
    • 重新启动定时器、动画或监听器。
    • 滚动位置恢复。

onDeactivated

  • 触发时机:组件被缓存(隐藏)时。
  • 用途
    • 暂停定时器、视频播放、音频播放。
    • 取消不必要的网络请求。
    • 保存临时状态。

4. 实战演示:代码与现象

让我们通过一个具体的例子来观察生命周期的执行顺序。

子组件 (CachedComponent.vue)

vue 复制代码
<template>
  <div class="box">
    <h3>当前计数: {{ count }}</h3>
    <button @click="count++">增加</button>
    <p>日志: {{ logs.join(" -> ") }}</p>
  </div>
</template>

<script setup>
import { ref, onMounted, onUnmounted, onActivated, onDeactivated } from "vue";

const count = ref(0);
const logs = ref([]);

const addLog = (msg) => {
  logs.value.push(msg);
  console.log(msg);
};

// 1. 初始化阶段
onMounted(() => {
  addLog("onMounted");
});

// 2. 激活阶段(首次进入 & 再次进入)
onActivated(() => {
  addLog("onActivated");
});

// 3. 停用阶段(切换离开)
onDeactivated(() => {
  addLog("onDeactivated");
});

// 4. 销毁阶段(只有当 keep-alive 排除该组件或父组件销毁时才触发)
onUnmounted(() => {
  addLog("onUnmounted");
});
</script>

父组件 (App.vue)

vue 复制代码
<template>
  <div>
    <button @click="toggle">切换组件</button>
    <keep-alive>
      <CachedComponent v-if="show" />
    </keep-alive>
  </div>
</template>

<script setup>
import { ref } from "vue";
import CachedComponent from "./CachedComponent.vue";

const show = ref(true);

const toggle = () => {
  show.value = !show.value;
};
</script>

🎬 现象观察

  1. 首次加载

    • 控制台输出:onMounted -> onActivated
    • 页面显示:计数为 0。
  2. 点击"切换组件"隐藏再显示

    • 控制台输出:onDeactivated (隐藏时) -> onActivated (显示时)
    • 注意onMounted 没有再次执行!
    • 页面显示:计数保持之前的值(例如你之前加到了 5,现在还是 5),证明组件状态被保留了。
  3. 最终销毁(如关闭标签页或移除 keep-alive)

    • 控制台输出:onDeactivated -> onUnmounted

⚠️ 常见坑点与最佳实践

❌ 坑点 1:在 onMounted 中请求数据,导致数据不更新

问题 :如果你只在 onMounted 中调用 API 获取列表数据,当用户从详情页返回列表页时,由于 onMounted 不执行,列表数据不会刷新,可能显示旧数据。

✅ 解决方案

  • 将数据请求逻辑放入 onActivated
  • 或者在 onActivated 中判断是否需要刷新。
javascript 复制代码
onActivated(() => {
  fetchData(); // 每次激活都重新获取最新数据
});

❌ 坑点 2:定时器/监听器未在 onDeactivated 中清理

问题 :在 onMounted 中启动了 setIntervalwindow.addEventListener,但在 onUnmounted 中清理。由于组件被缓存时不会触发 onUnmounted,导致定时器在后台继续运行,造成内存泄漏或性能问题。

✅ 解决方案

  • onDeactivated 中暂停或清理资源。
  • onActivated 中重新启动。
javascript 复制代码
let timer = null;

onActivated(() => {
  timer = setInterval(() => {
    console.log("tick");
  }, 1000);
});

onDeactivated(() => {
  if (timer) clearInterval(timer);
});

❌ 坑点 3:误以为 keep-alive 会缓存所有状态

注意keep-alive 缓存的是组件实例 。如果组件内部使用了非响应式变量(如普通 let 变量),或者依赖外部全局状态但未正确订阅,可能会出现预期外的行为。务必确保状态管理的一致性。


💡 总结

特性 普通组件 Keep-Alive 组件
创建/挂载 每次切换都执行 仅首次执行
销毁/卸载 每次切换都执行 仅最终移除时执行
激活/停用 onActivated / onDeactivated
状态保留 不保留 保留 (包括 data, computed, DOM 状态)
适用场景 一次性页面、简单组件 标签页、表单、高频切换列表

🚀 最佳实践口诀:

缓存组件要记牢,Mounted 只跑第一遭。

数据刷新找 Activated,资源清理 Deactivated 跑。

定时器、监听器,切换隐藏记得消。

希望这篇文档能帮你彻底搞定 Vue 3 中的 keep-alive 生命周期!如果有疑问,欢迎留言交流。👇

觉得有用吗?点赞、收藏、关注,获取更多 Vue 进阶干货! ❤️

相关推荐
jiayong232 小时前
第 33 课:任务看板视图(按状态分列)与本地持久化
开发语言·前端·javascript·学习
GISer_Jing2 小时前
Dify可视化编排:技术架构与实战指南
前端·人工智能·ai编程
宇宙realman_9992 小时前
DSP28335-FlashAPI使用
linux·前端·python
踩着两条虫2 小时前
VTJ 平台六大设计模式落地实战指南
开发语言·前端·人工智能·低代码·设计模式·重构·架构
Yeats_Liao2 小时前
后台 Sidebar 伸缩交互(PC + 移动端)实现
前端·javascript·css·html5
MXN_小南学前端2 小时前
computed 计算属性详解:触发时机、实战场景、Vue2 与 Vue3 对比
前端·javascript·vue.js
isNotNullX3 小时前
数据大屏怎么做?数据大屏有哪四个核心环节
开发语言·前端·javascript
漫游的渔夫3 小时前
RAG 落地 3 个月,我才发现排序(Rerank)比检索更重要
前端·人工智能
衣乌安、3 小时前
Agent之ReAct
前端·ai