Vue 3 的 keep-alive 及生命周期钩子
在 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 的值设置为 组件的引用。
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 提供的一个优化方法,适用于那些不需要响应式处理的对象(比如组件)。
// 使用 markRaw 来避免组件变成响应式
const currentComponent = ref(markRaw(ComponentA));
2、使用 shallowRef(推荐)
shallowRef 是 Vue 3 提供的一个 API,类似于 ref,但它只会让引用的对象本身变得响应式,而不会递归地将对象中的嵌套属性变成响应式。对于 Vue 组件,使用 shallowRef 会更加合适,因为我们只关心组件本身的引用,而不需要其内部的响应式。
// 使用 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 中进行处理。
onDeactivated(() => {// 停止任何异步任务或计时器clearInterval(someTimer)console.log('停止异步任务')
})
2、性能问题
如果 onActivated 钩子执行了某些耗时操作(如数据请求、定时任务等),可能会影响用户体验。为了避免影响性能,可以在 onActivated 中进行懒加载操作,只在需要时执行某些任务。
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>
路由配置:
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 钩子函数执行一些操作。
相关文章:

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

ComfyUI实现老照片修复——AI修复老照片(ComfyUI-ReActor / ReSwapper)解决天坑问题及加速pip下载
AI修复老照片,试试吧,不一定好~~哈哈 2023年4月曾用过ComfyUI,当时就感慨这个工具和虚幻的蓝图很像,以后肯定是专业人玩的。 2024年我写代码去了,AI做图没太关注,没想到,现在ComfyUI真的变成了工…...

OpenEuler学习笔记(十一):OpenEuler上搭建LAMP环境
LAMP环境指的是Linux、Apache、MySQL(或MariaDB)和PHP的组合,下面为你介绍在OpenEuler上搭建LAMP环境的详细步骤: 1. 系统更新 首先要更新系统中的软件包,保证系统处于最新状态。 sudo dnf update -y2. 安装Apache…...

Mongodb 慢查询日志分析 - 1
Mongodb 慢查询日志分析 使用 mloginfo 处理过的日志会在控制台输出, 显示还是比较友好的. 但是如果内容较大, 就不方便查看了, 如果可以导入到 excel 就比较方便筛选/排序. 但是 mloginfo 并没有提供生成到 excel 的功能. 可以通过一个 python 脚本辅助生成: import pandas…...

MySQL面试题2025 每日20道【其四】
1、你们生产环境的 MySQL 中使用了什么事务隔离级别?为什么? 中等 在生产环境中,MySQL数据库的事务隔离级别通常由开发团队或数据库管理员根据应用的需求来设定。MySQL支持四种标准的事务隔离级别: 读未提交(Read Unc…...

微服务学习-Nacos 注册中心实战
1. 注册中心的设计思路 1.1. 微服务为什么会用到注册中心? 服务与服务之间调用需要有服务发现功能;例如订单服务调用库存服务,库存服务如果有多个,订单服务到底调用那个库存服务呢(负载均衡器)࿰…...

k8s服务StatefulSet部署模板
java 服务StatefulSet部署模板 vim templates-test.yamlapiVersion: apps/v1 kind: StatefulSet metadata:labels:app: ${app_labels}name: ${app_name}namespace: ${app_namespace} spec:replicas: ${app_replicas_count}selector:matchLabels:app: ${app_labels}template:la…...

07 区块链安全技术
概述 区块链的安全特性 区块链解决了在不可靠网络上可靠地传输信息的难题,由于不依赖与中心节点的认证和管理,因此防止了中心节点被攻击造成的数据泄露和认证失败的风险。 区块链安全防护的三大特点 共识机制代替中心认证机制数据篡改“一发动全身”…...

Adobe的AI生成3D数字人框架:从自拍到生动的3D化身
一、引言 随着人工智能技术的发展,我们见证了越来越多创新工具的出现,这些工具使得图像处理和视频编辑变得更加智能与高效。Adobe作为全球领先的创意软件公司,最近推出了一项令人瞩目的新技术——一个能够将普通的二维自拍照转换成栩栩如生的三维(3D)数字人的框架。这项技…...

dfs专题四:综合练习
key:画出决策树(就是找个简单例子模拟一下的树状决策图) dfs传参 or 全局变量: int, double等常量/比较小的变量,可以dfs参数传递vector等线性O(N)变量,要用全局变量 回溯&#x…...

【线性代数】列主元法求矩阵的逆
列主元方法是一种用于求解矩阵逆的数值方法,特别适用于在计算机上实现。其基本思想是通过高斯消元法将矩阵转换为上三角矩阵,然后通过回代求解矩阵的逆。以下是列主元方法求解矩阵 A A A 的逆的步骤: [精确算法] 列主元高斯消元法 步骤 1&am…...

大写——蓝桥杯
1.题目描述 给定一个只包含大写字母和小写字母的字符串,请将其中所有的小写字母转换成大写字母后将字符串输出。 输入描述 输入一行包含一个字符串。 输出描述 输出转换成大写后的字符串。 输入输出样例 示例 输入 LanQiao输出 LANQIAO评测用例规模与约定 对…...

HTML `<head>` 元素详解
在 HTML 文档中,<head> 元素是一个非常重要的部分,它包含了文档的元数据(metadata)和其他与文档相关的信息。虽然 <head> 中的内容不会直接显示在网页上,但它对网页的行为、样式和搜索引擎优化(…...

一文速通stack和queue的理解与使用
CSTL之stack和queue 1.stack1.1.stack的基本概念1.2.stack的接口 2.queue2.1.queue的基本概念2.2.queue的接口 3.priority_queue3.1.priority_queue的基本概念3.2.priority_queue的接口3.3.仿函数 4.容器适配器5.deque5.1.deque的简单了解5.2.deque的优缺点 🌟&…...

Antd React Form使用Radio嵌套多个Select和Input的处理
使用Antd React Form使用Radio会遇到嵌套多个Select和Input的处理,需要多层嵌套和处理默认事件和冒泡,具体实现过程直接上代码。 实现效果布局如下图 代码 <Formname"basic"form{form}labelWrap{...formItemLayoutSpan(5, 19)}onFinish{on…...

Vue - toRefs() 和 toRef() 的使用
一、toRefs() 在 Vue 3 中,toRefs()可以将响应式对象的属性转换为可响应的 refs。主要用于在解构响应式对象时,保持属性的响应性。 1. 导入 toRefs 函数 import { toRefs } from vue;2. 将响应式对象的属性转换为 ref const state reactive({count: 0,message:…...

Python3 OS模块中的文件/目录方法说明九
一. 简介 前面文章简单学习了 Python3 中 OS模块中的文件/目录的部分函数。 本文继续来学习 OS 模块中文件、目录的操作方法:os.pipe() 方法、os.popen() 方法。 二. Python3 OS模块中的文件/目录方法 1. os.pipe() 方法 os.pipe() 方法用于创建一个管道, 返回…...

OpenCV文字绘制支持中文显示
OpenCV版本:4.4 IDE:VS2019 功能描述 OpenCV绘制文本的函数putText()不支持中文的显示,网上很多方法推荐的都是使用FreeType来支持,FreeType是什么呢?FreeType的官网上有介绍 FreeType官网 https://www.freetype.or…...

opengrok_windows_多工程环境搭建
目录 多工程的目录 工程代码下载和log配置 工程的索引 工程部署 工程测试 参考列表 多工程的目录 工程代码下载和log配置 工程代码下载 在每个工程的src目录下,下载工程代码,以下载pulseaudio的代码为例。 git clone gitgithub.com…...

基于ollama,langchain,springboot从零搭建知识库三【解析文档并存储到向量数据库】
安装环境 安装pgvector,先设置docker镜像源: vim /etc/docker/daemon.json {"registry-mirrors": ["https://05f073ad3c0010ea0f4bc00b7105ec20.mirror.swr.myhuaweicloud.com","https://mirror.ccs.tencentyun.com",&…...

Elasticsearch 和arkime 安装
安装一定要注意版本号,不然使用不了 这里Ubuntu使用ubuntu-20.04.6-desktop-amd64.iso elasticsearch这里使用Elasticsearch 7.17.5 | Elastic arkime这里使用wget https://s3.amazonaws.com/files.molo.ch/builds/ubuntu-20.04/arkime_3.4.2-1_amd64.deb 大家想…...

git回退
git回退 1、未使用 git add 缓存代码时 git checkout –- filepathname 放弃单个文件的修改 git checkout . 放弃所有的文件修改 此命令用来放弃掉所有还没有加入到缓存区(就是 git add 命令)的修改:内容修改与整个文件删除。但是此命令不…...

pytest+playwright落地实战大纲
前言 很久没有更新博客,是因为在梳理制作Playwright测试框架实战相关的课程内容。现在课程已经完结,开个帖子介绍下这门课程(硬广, o(〃^▽^〃)o) 课程放在CSDN学习频道, 欢迎关注~ PyTestPl…...

01-硬件入门学习/嵌入式教程-CH340C使用教程
前言 CH340C广泛应用于DIY项目和嵌入式开发中,用于USB数据转换和串口通信。本文将详细介绍CH340C的基本功能、引脚接线及使用方法。 CH340C简介 CH340C是一款USB转TTL电平转换器,可以将电脑的USB数据转换成串口数据,方便与单片机ÿ…...

小试牛刀调整Prompt,优化Token消耗
在上一篇文章 荒腔走板Mac电脑本地部署 LLM 中介绍过本地部署大模型之后,可以通过定制 prompt 来实现 domain 提取等各种各样的需求。 但是实际上,部署本地大模型 这种方式对于个人开发者来说实在是不太友好。一方面需要投入大量资金确保设备的算力足够支…...

snippets router pinia axios mock
文章目录 补充VS Code 代码片段注册自定义组件vue routerpinia删除vite创建项目时默认的文件axiosmock3.0.x版本的 viteMockServe 补充 为文章做补充:https://blog.csdn.net/yavlgloss/article/details/140063387 VS Code 代码片段 为当前项目创建 Snippets {&quo…...

Visual Studio2019调试DLL
1、编写好DLL代码之后,对DLL项目的属性进行设置,选择待注入的DLL,如下图所示 2、生成DLL文件 3、将DLL设置为启动项目之后,按F5启动调试。弹出选择注入的exe的界面之后,使用代码注入器注入步骤2中生成的dll࿰…...

深入解析:Docker 容器如何实现文件系统与资源的多维隔离?
目录 一、RootFs1. Docker 镜像与文件系统层2. RootFs 与容器隔离的意义 二、Linux Namespace1. 进程命名空间1.1 lsns 命令说明1.2 查看“祖先进程”命名空间1.3 查看当前用户进程命名空间 2. 容器进程命名空间2.1 查看容器进程命名空间列表2.2 容器进程命名空间的具体体现 三…...

vue项目中打包后的地址加载不出图片【五种解决方案】
在 Vue 项目中打包后,加载图片路径可能会出现问题,主要是因为打包后的路径与开发时的路径不同。为了确保图片可以正确加载,你可以考虑以下几种方法: 1. 使用 require 或 import 动态加载图片 如果你在 Vue 的模板或者脚本中引用…...

讯飞星火大模型将超越chatgpt?
讯飞星火大模型真的能超越ChatGPT吗? 在人工智能的世界里,新技术层出不穷,而科大讯飞最近发布的讯飞星火大模型3.0引发了不少讨论。有些人甚至大胆猜测:这个模型是否能够在某些方面超越如今广受欢迎的ChatGPT?今天,我们就来深入探讨一下这个话题,分析讯飞星火大模型3.0…...