当前位置: 首页 > news >正文

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 钩子函数执行一些操作。

http://www.lryc.cn/news/525792.html

相关文章:

  • ComfyUI实现老照片修复——AI修复老照片(ComfyUI-ReActor / ReSwapper)解决天坑问题及加速pip下载
  • OpenEuler学习笔记(十一):OpenEuler上搭建LAMP环境
  • Mongodb 慢查询日志分析 - 1
  • MySQL面试题2025 每日20道【其四】
  • 微服务学习-Nacos 注册中心实战
  • k8s服务StatefulSet部署模板
  • 07 区块链安全技术
  • Adobe的AI生成3D数字人框架:从自拍到生动的3D化身
  • dfs专题四:综合练习
  • 【线性代数】列主元法求矩阵的逆
  • 大写——蓝桥杯
  • HTML `<head>` 元素详解
  • 一文速通stack和queue的理解与使用
  • Antd React Form使用Radio嵌套多个Select和Input的处理
  • Vue - toRefs() 和 toRef() 的使用
  • Python3 OS模块中的文件/目录方法说明九
  • OpenCV文字绘制支持中文显示
  • opengrok_windows_多工程环境搭建
  • 基于ollama,langchain,springboot从零搭建知识库三【解析文档并存储到向量数据库】
  • Elasticsearch 和arkime 安装
  • git回退
  • pytest+playwright落地实战大纲
  • 01-硬件入门学习/嵌入式教程-CH340C使用教程
  • 小试牛刀调整Prompt,优化Token消耗
  • snippets router pinia axios mock
  • Visual Studio2019调试DLL
  • 深入解析:Docker 容器如何实现文件系统与资源的多维隔离?
  • vue项目中打包后的地址加载不出图片【五种解决方案】
  • 讯飞星火大模型将超越chatgpt?
  • 3D Vision--计算点到平面的距离