Vue 3 的 keep-alive 及生命周期钩子

server/2025/1/23 7:29:16/

在 Vue 3 中,keep-alive 是一个内置组件,用于提高性能和减少不必要的组件销毁与重建。它与组件的生命周期紧密相关,特别是在动态组件和路由切换场景下,能够缓存组件的状态并避免重新渲染。 

onActivated 和 onDeactivated 是 Vue3 Composition API 提供的钩子函数,专门用于处理被 keep-alive 缓存组件的激活和销毁逻辑。

1. keep-alive

1.1 什么是 keep-alive?

在 Vue 中,keep-alive 是一个内置的高阶组件,用于缓存不再活跃的组件实例,使其在不再渲染时依然保留状态。通过,在需要频繁切换视图组件或动态组件时使用 keep-alive,这样可以避免频繁销毁和重新创建组件,从而提高性能。

eg:很多情况下,当在多个选项卡或页面之间切换时,可能会希望保留每个选项卡的状态,而不是每次切换时都销毁再重新加载它们。此时,keep-alive 就可以解决这个问题。

1.2 基本使用

keep-alive 可以包裹动态组件或页面,当使用时,它会缓存已渲染的组件,并将其状态保持在内存中。当组件再次被激活时,keep-alive 会复用该组件的实例,而不是重新创建一个新的组件。

🌰 子组件 A + B 类似

<template><div><h2>这是组件A</h2><p>这是组件A的内容</p></div>
</template><script>
export default {name: 'ComponentA',mounted() {console.log('ComponentA 被挂载');},unmounted() {console.log('ComponentA 被卸载');},
};
</script>

App. vue

<template><div><button @click="switchComponent">切换组件</button><keep-alive><component :is="currentComponent"></component></keep-alive></div>
</template><script>
import { defineComponent, ref } from 'vue';
import ComponentA from './components/ComponentA.vue';
import ComponentB from './components/ComponentB.vue';export default defineComponent({name: 'App',setup() {// 当前渲染的组件,直接引用组件const currentComponent = ref(ComponentA);// 切换组件的逻辑const switchComponent = () => {currentComponent.value = JSON.stringify(currentComponent.value) === JSON.stringify(ComponentA) ? ComponentB : ComponentA;};return {currentComponent,switchComponent,};},
});
</script>

展示如下:

点击切换

切换后,A 组件被缓存而不是被销毁,再次切换回来 B 组件被缓存,A 组件重新渲染。

如果不使用 keep-alive 表现为:

<template><div><button @click="switchComponent">切换组件</button><component :is="currentComponent"></component></div>
</template>

组件先被销毁然后创建。 

小 Tip 1

下面写法展示组件失败,为什么呢?

根本原因是,在 setup() 中使用的是字符串 'ComponentA' 和 'ComponentB',而在 keep-alive 和 component 组件的 :is 属性中,Vue 期望的是组件本身,而不是它的字符串名称。

因此需要将 currentComponent 的值设置为 组件的引用。

javascript">export default defineComponent({name: 'App',setup() {// 当前渲染的组件const currentComponent = ref('ComponentA');// 切换组件的逻辑const switchComponent = () => {currentComponent.value = currentComponent.value === 'ComponentA' ? 'ComponentB' : 'ComponentA';};return {currentComponent,switchComponent,};},
});
小 Tip 2

上面图片右侧控制台出现黄色警告⚠️,说明在 ref 中存储了一个 Vue 组件实例的响应式对象,而 Vue 会自动将其变成响应式,这可能会带来性能问题,为了避免这个情况,Vue 建议使用 markRaw 或 shallowRef 避免组件变成响应式对象。

解决方法

1、使用 markRaw

markRaw 会阻止 Vue 对传入对象进行响应式处理,它是 Vue 提供的一个优化方法,适用于那些不需要响应式处理的对象(比如组件)。

javascript">// 使用 markRaw 来避免组件变成响应式
const currentComponent = ref(markRaw(ComponentA));

2、使用 shallowRef(推荐)

shallowRef 是 Vue 3 提供的一个 API,类似于 ref,但它只会让引用的对象本身变得响应式,而不会递归地将对象中的嵌套属性变成响应式。对于 Vue 组件,使用 shallowRef 会更加合适,因为我们只关心组件本身的引用,而不需要其内部的响应式。

javascript">// 使用 shallowRef 来避免组件变成深度响应式
const currentComponent = shallowRef(ComponentA);

1.3 include 和 exclude 属性

keep-alive 还提供了 include 和 exclude 属性,用于控制哪些组件应该被缓存,哪些组件不应该被缓存。

- include:一个字符串、正则表达式或数组,指定哪些组件应该被缓存。

- exclude:一个字符串、正则表达式或数组,指定哪些组件不应该被缓存。

🌰

<template><div><button @click="switchComponent">切换组件</button><keep-alive :include="['ComponentA']" :exclude="['ComponentB']"><component :is="currentComponent"></component></keep-alive></div>
</template>
<script>
import { defineComponent, shallowRef } from 'vue';
import ComponentA from './components/ComponentA.vue';
import ComponentB from './components/ComponentB.vue';export default defineComponent({name: 'App',setup() {const currentComponent = shallowRef(ComponentA);const switchComponent = () => {currentComponent.value = JSON.stringify(currentComponent.value) === JSON.stringify(ComponentA) ? ComponentB : ComponentA;};return {currentComponent,switchComponent,};},
});
</script>

只有 ComponentA 会被缓存,ComponentB 不会。根据实际需要灵活控制哪些组件需要被缓存。

2. 钩子函数

2.1 onActivated

onActivated 钩子会在组件被缓存并重新激活时触发。通常情况下,组件被 keep-alive 缓存后,如果用户切换到该组件,Vue 会复用这个组件的实例并调用 onActivated 钩子。

使用场景:需要在组件激活时重新加载数据、执行某些操作(eg:开启动画、更新状态等)。

2.2 onDeactivated

与 onActivated 类似,onDeactivated 会在组件被缓存并从 keep-alive 中移除时触发。此时,组件的状态会被保留,但它的 DOM 和实例会被销毁,当再次激活时,onActivated 会重新触发。

使用场景:需要在组件去激活时清理资源,例如停止计时器、清理事件监听器等。或进行一些状态重置操作。

2.3 🌰

注意:这两个钩子函数只有在组件被 <keep-alive> 包裹,且组件经历过激活和停用的过程,也就是组件需要在显示和隐藏之间切换,而不是一直保持显示状态。

子组件

<template><div><h3>子组件</h3></div>
</template><script setup>
import { onActivated, onDeactivated } from 'vue';// 在组件激活时重新加载数据
onActivated(() => {console.log('组件被激活,重新加载数据');
});// 在组件停用时清理数据
onDeactivated(() => {console.log('组件被停用,清理数据或停止操作');
});
</script>

App.vue

<template><div><keep-alive><Info v-if="isActive" /></keep-alive><button @click="toggleActive">切换显示</button></div>
</template><script setup>
import { ref } from 'vue';
import Info from './components/Info.vue';const isActive = ref(true);const toggleActive = () => {isActive.value = !isActive.value;
};
</script>

展示为:

小 Tip 3 

上面🌰使用 v-if 控制组件的显示和隐藏,如果使用 v-show 呢?

<template><div><button @click="toggleActive">切换组件显示</button><keep-alive><UserInfo v-show="isActive" /></keep-alive></div>
</template>

结果是:不会触发。

如果使用 v-show 控制组件显示和隐藏,不会触发两个钩子的执行,因为其是通过修改 display 属性来显示或隐藏 DOM 元素,而不会销毁和重新渲染组件。

2.4 存在问题 

1、缓存时的副作用

当组件被缓存时,它的状态会保留在内存中,不会被销毁。如果需要清理某些副作用(例如,清理计时器、停止网络请求等),应该在 onDeactivated 中进行处理。

javascript">onDeactivated(() => {// 停止任何异步任务或计时器clearInterval(someTimer)console.log('停止异步任务')
})

2、性能问题

如果 onActivated 钩子执行了某些耗时操作(如数据请求、定时任务等),可能会影响用户体验。为了避免影响性能,可以在 onActivated 中进行懒加载操作,只在需要时执行某些任务。

javascript">onActivated(() => {if (!info.value) {fetchData()  // 如果没有数据才加载}
})

3. keep-alive 与路由结合 

3.1 在 Vue 路由中使用 keep-alive

Vue 路由通常与 keep-alive 一起使用,特别是在单页面应用(SPA)中。当切换路由时,希望某些页面保持活跃并缓存其状态。keep-alive 可以用来缓存路由视图组件,避免在路由切换时销毁和重新渲染。

<template><keep-alive><router-view></router-view></keep-alive>
</template><script>
export default {// 使用 keep-alive 包裹 router-view,使得路由切换时组件能够缓存
};
</script>

router-view 是 Vue 路由用于渲染当前匹配的路由组件,keep-alive 缓存路由组件,当路由切换时,之前的组件不会被销毁,而是保持活跃并缓存状态。

3.2 动态路由

keep-alive 的 include 和 exclude 属性可以用来动态控制哪些路由视图组件应该被缓存,哪些不应该被缓存。

<template><div><h1>动态路由和 keep-alive</h1><keep-alive :include="['Home', 'About']"><router-view></router-view></keep-alive></div>
</template><script>
export default {data() {return {// 只有 Home 和 About 组件会被缓存}},
}
</script>

路由配置:

javascript">import { createRouter, createWebHistory } from 'vue-router';
import Home from './components/Home.vue';
import About from './components/About.vue';
import Contact from './components/Contact.vue';const routes = [{path: '/',name: 'Home',component: Home,},{path: '/about',name: 'About',component: About,},{path: '/contact',name: 'Contact',component: Contact,},
];const router = createRouter({history: createWebHistory(),routes,
});export default router;

同理:

<keep-alive :exclude="['Contact']"><router-view></router-view>
</keep-alive>

根据具体情况来控制缓存策略,从而有效提升性能,并节省内存。

总结:使用 keep-alive 时,路由切换时被缓存的组件会保持其状态。例如,表单输入的数据、滚动位置等都不会丢失。为了确保在缓存组件时能保持状态,可以使用 onActivated 和 onDeactivated 钩子函数执行一些操作。


http://www.ppmy.cn/server/160672.html

相关文章

ubuntu20.04搭建wordpress时出现问题汇总

主要参考教材&#xff1a;ubuntu20.04安装wordpress教程 问题1&#xff1a; rootracknerd-e7ec47c:~# sudo apt-get install php8.1 Reading package lists... Done Building dependency tree Reading state information... Done E: Unable to locate package php8.1 E…

【25考研】也很难!清华大学计算机考研复试难度分析!

一、复试内容 复试考核注意事项&#xff1a; 1、笔试环节&#xff1a;笔试部分包括英语和专业课的考查。其中英语笔试部分把包括英语听力和口语测试&#xff1b;关于专业课考试&#xff0c;有的学校规定了考试范围&#xff0c;考生可以在初试结束后尽快开始复习&#xff1b;对…

电子应用设计方案101:智能家庭AI喝水杯系统设计

智能家庭 AI 喝水杯系统设计 一、引言 智能家庭 AI 喝水杯系统旨在为用户提供个性化的饮水提醒和健康管理服务&#xff0c;帮助用户养成良好的饮水习惯。 二、系统概述 1. 系统目标 - 精确监测饮水量和饮水频率。 - 根据用户的身体状况和活动量&#xff0c;智能制定饮水计划。…

jenkins-通过api获取所有job及最新build信息

日常维护的API接口功能&#xff1a; 前提&#xff1a; python 1. 获取所有job&#xff1a; def get_all_jobs(jenkins_url, username, password):url f"{jenkins_url}/api/json"response requests.get(url, auth(username, password))if response.status_code 2…

Unity3D项目开发中的资源加密详解

前言 在Unity3D游戏开发中&#xff0c;保护游戏资源不被非法获取和篡改是至关重要的一环。资源加密作为一种有效的技术手段&#xff0c;可以帮助开发者维护游戏的知识产权和安全性。本文将详细介绍Unity3D项目中如何进行资源加密&#xff0c;并提供相应的技术详解和代码实现。…

面试题-Spring Cloud(25道)

Spring Cloud核心知识总结 下面是一张Spring Cloud核心组件关系图&#xff1a; 1、什么是Spring Cloud &#xff1f; Spring cloud 流应用程序启动器是基于 Spring Boot 的 Spring 集成应用程序&#xff0c;提供与外部系统的集成。Spring cloud Task&#xff0c;一个生命周期短…

C++实现有限元二维杆单元计算 Bar2D2Node类(纯自研 非套壳)

本系列文章致力于实现“手搓有限元&#xff0c;干翻Ansys的目标”&#xff0c;基本框架为前端显示使用QT实现交互&#xff0c;后端计算采用Visual Studio C。 QT软件界面 具体软件操作可查看下方视频哦。也可以点击这里直接跳转。 直接干翻Ansys&#xff1f;小伙自研有限元 1、…

CentOS 7中 分区工具fdisk的常用命令【解释来自gpt】

在CentOS 7中&#xff0c;fdisk工具用于对磁盘进行分区操作。以下是fdisk命令及其相关操作的中文解释&#xff1a; 启动 fdisk 工具 命令&#xff1a; fdisk /dev/sdX 该命令启动fdisk工具&#xff0c;/dev/sdX是你要管理的磁盘&#xff08;例如/dev/sda&#xff09;。 常用…