首页 > 编程笔记 > JavaScript笔记
阅读:11
Vue KeepAlive组件用法详解(附带实例)
Vue 为动态加载的组件提供了一种性能优化方案——缓存组件,其利用内置组件 KeepAlive 实现。
组件在加载时会经历初始、挂载、更新、销毁生命周期,对于动态组件加载来说,频繁地切换组件会不断地重复组件的初始、挂载、销毁生命周期,这意味着程序需要不断地读取和释放内存,因此将会极大地影响内存开销,从而影响项目的性能。
如果可以将这样的组件暂存于内存中不做释放处理,使用的时候再从内存中取出,就可以省去组件创建和销毁等耗费内存的操作。Vue 提供了 KeepAlive 内置组件来实现这一目标。
实现缓存组件很简单,只需利用 KeepAlive 将 Component 动态组件包裹即可,但是如何才能确认缓存组件的运行呢?这需要通过子组件的生命周期才能够看出。下面通过代码来演示这个过程。
为 Comp1、Comp2、Comp3 这 3 个子组件添加生命周期钩子函数,并输出对应的字符串进行测试。这里以 Comp1.vue 文件代码为例。

图 1 没有触发Comp1的onBeforeUnmount、onUnmounted生命周期钩子函数
引入两个新的生命周期钩子函数 onActivated、onDeactivated 来确认缓存组件的运行结果。添加生命周期钩子函数后,初始 Comp1 触发的生命周期钩子函数变为 3 个,多了一个用于激活组件状态的生命周期钩子函数 onActivated,如下图所示。

图 2 Comp1组件触发的生命周期钩子函数变为3个
添加的代码如下所示:

图 3 离开时触发了onDeactivated生命周期钩子函数
说明 Comp1 已经被暂存于内存中,并没有从内存中销毁。Comp2 切换到 Comp3 的过程同理。
到目前为止,利用 KeepAlive 内置组件可以对所有动态加载的组件进行缓存。如果缓存组件的内容很多,那么也会给内存带来一定的压力,因为内存的存储空间是有限的,所以控制目标组件的缓存显得尤为重要。
因此,KeepAlive 内置组件提供了 include 和 exclude 两个属性用来解决这个困扰:
这两个属性值都可以设置为字符串、正则表达式与数组。
虽然 KeepAlive 内置组件有 include 和 exclude 属性,但是包含谁、排除谁,尚未可知。当前有 Comp1、Comp2、Comp3 组件,如何让 include 和 exclude 属性明确其包含与排除的目标呢?此时就需要配合组件的 name 名称来实现。
值得一提的是,因为在 Vue3 中 <script setup> 的脚本部分应用了组合式 API,所以并没有提供给组件设置 name 名称的功能。如果想要给组件设置名称,那么可以在组件中添加一个 script 脚本,利用选项式 API 为其添加 name 属性,比如,可以为 Comp1、Comp2、Comp3 组件添加下方代码,以 Comp1 组件为例。

图 4 include和exclude属性值都是undefined(未定义)
下面尝试给 KeepAlive 内置组件设置 include 属性,属性值可以设置为字符串、正则表达式和数组 3 种模式。在通常情况下,我们会选择最容易理解的数组模式。
下面展示 3 种模式的书写代码。
1) 字符串模式
2) 正则表达式模式
3) 数组模式

图 5 通过控制台验证Comp3组件是否被缓存
通过图 5 可以看出,Comp3组件没有被缓存。
当然,也可以通过 Vue 开发者调试工具验证 Comp3 组件是否被缓存。我们可以观察到 Vue 开发者调试工具中的 Comp3 组件没有 inactive 的状态显示,因此 Comp3 组件没有被缓存,如下图所示。

图 6 通过Vue开发者调试工具验证Comp3组件是否被缓存
动画内置组件 Transition 与 TransitionGroup,配合动态加载组件 Component 与缓存组件 KeepAlive 可以实现动画效果。
以 Transition 组件为例,在项目根目录的 index.html 文件中添加下方代码,引入 animate.css 动画类库。
组件在加载时会经历初始、挂载、更新、销毁生命周期,对于动态组件加载来说,频繁地切换组件会不断地重复组件的初始、挂载、销毁生命周期,这意味着程序需要不断地读取和释放内存,因此将会极大地影响内存开销,从而影响项目的性能。
如果可以将这样的组件暂存于内存中不做释放处理,使用的时候再从内存中取出,就可以省去组件创建和销毁等耗费内存的操作。Vue 提供了 KeepAlive 内置组件来实现这一目标。
实现缓存组件很简单,只需利用 KeepAlive 将 Component 动态组件包裹即可,但是如何才能确认缓存组件的运行呢?这需要通过子组件的生命周期才能够看出。下面通过代码来演示这个过程。
<template> <button @click="changeTab(Comp1)">ChangeComp1</button> <button @click="changeTab(Comp2)">ChangeComp2</button> <button @click="changeTab(Comp3)">ChangeComp3</button> <keep-alive> <component :is="tab"></component> </keep-alive> </template> <script setup> import Comp1 from './components/Comp1.vue'; import Comp2 from './components/Comp2.vue'; import Comp3 from './components/Comp3.vue'; import { ref, markRaw } from 'vue'; // 设置需要切换的组件,初始为Comp1,使用markRaw函数,不对组件进行递归响应式数据代理 const tab = ref(markRaw(Comp1)); // 定义切换组件函数,将组件本身当成参数传递 function changeTab(comp) { tab.value = markRaw(comp); } // 默认切换显示Comp1组件 changeTab(Comp1); </script>
为 Comp1、Comp2、Comp3 这 3 个子组件添加生命周期钩子函数,并输出对应的字符串进行测试。这里以 Comp1.vue 文件代码为例。
<template> <h1>Comp 1</h1> </template> <script setup> import { onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted, onActivated, onDeactivated, } from 'vue'; onBeforeMount(() => { console.log('Comp1 onBeforeMount'); }); onMounted(() => { console.log('Comp1 onMounted'); }); onBeforeUpdate(() => { console.log('Comp1 onBeforeUpdate'); }); onUpdated(() => { console.log('Comp1 onUpdated'); }); onBeforeUnmount(() => { console.log('Comp1 onBeforeUnmount'); }); onUnmounted(() => { console.log('Comp1 onUnmounted'); }); </script>运行代码会发现在 Comp1 切换至 Comp2 时,两个组件的生命周期都只是执行了挂载的 onBeforeMount、onMounted 生命周期钩子函数。现在已经离开了 Comp1,而 Comp1 中的 onMounted、onBeforeUnmounted 生命周期钩子函数并没有被触发,说明当前的组件已经被缓存了,如下图所示。

图 1 没有触发Comp1的onBeforeUnmount、onUnmounted生命周期钩子函数
引入两个新的生命周期钩子函数 onActivated、onDeactivated 来确认缓存组件的运行结果。添加生命周期钩子函数后,初始 Comp1 触发的生命周期钩子函数变为 3 个,多了一个用于激活组件状态的生命周期钩子函数 onActivated,如下图所示。

图 2 Comp1组件触发的生命周期钩子函数变为3个
添加的代码如下所示:
// 新添加的生命周期钩子函数,只有在KeepAlive组件使用时才会被触发 // 组件激活时被触发 onActivated(() => { console.log('Comp1 onActivated'); }); // 组件失活时被触发 onDeactivated(() => { console.log('Comp1 onDeactivated'); });在 Comp1 切换到 Comp2 时,也没有触发 onBeforeUnmount 和 onUnmounted 生命周期钩子函数,而是触发了组件失活的 onDeactivated 生命周期钩子函数,如下图所示:

图 3 离开时触发了onDeactivated生命周期钩子函数
说明 Comp1 已经被暂存于内存中,并没有从内存中销毁。Comp2 切换到 Comp3 的过程同理。
到目前为止,利用 KeepAlive 内置组件可以对所有动态加载的组件进行缓存。如果缓存组件的内容很多,那么也会给内存带来一定的压力,因为内存的存储空间是有限的,所以控制目标组件的缓存显得尤为重要。
因此,KeepAlive 内置组件提供了 include 和 exclude 两个属性用来解决这个困扰:
- include 中文翻译为“包含”,用于确认哪些组件需要缓存;
- exclude 中文翻译为“排除”,用于排除不需要进行暂存处理的组件。
这两个属性值都可以设置为字符串、正则表达式与数组。
虽然 KeepAlive 内置组件有 include 和 exclude 属性,但是包含谁、排除谁,尚未可知。当前有 Comp1、Comp2、Comp3 组件,如何让 include 和 exclude 属性明确其包含与排除的目标呢?此时就需要配合组件的 name 名称来实现。
值得一提的是,因为在 Vue3 中 <script setup> 的脚本部分应用了组合式 API,所以并没有提供给组件设置 name 名称的功能。如果想要给组件设置名称,那么可以在组件中添加一个 script 脚本,利用选项式 API 为其添加 name 属性,比如,可以为 Comp1、Comp2、Comp3 组件添加下方代码,以 Comp1 组件为例。
<script> export default { name: 'Comp1', }; </script>此时可以通过 Vue 开发者调试工具确认当前项目的运行状态,在调试面板中可以明确 KeepAlive 内置组件的 include 和 exclude 属性值都是 undefined(未定义),如下图所示。

图 4 include和exclude属性值都是undefined(未定义)
下面尝试给 KeepAlive 内置组件设置 include 属性,属性值可以设置为字符串、正则表达式和数组 3 种模式。在通常情况下,我们会选择最容易理解的数组模式。
下面展示 3 种模式的书写代码。
1) 字符串模式
<keep-alive include="Comp1,Comp2"> <component :is="tab"></component> </keep-alive>
2) 正则表达式模式
<keep-alive :include="/Comp1|Comp2/"> <component :is="tab"></component> </keep-alive>
3) 数组模式
<keep-alive :include="[ 'Comp1', 'Comp2' ]"> <component :is="tab"></component> </keep-alive>此时,通过控制台验证 Comp3 组件是否被缓存,如下图所示。

图 5 通过控制台验证Comp3组件是否被缓存
通过图 5 可以看出,Comp3组件没有被缓存。
当然,也可以通过 Vue 开发者调试工具验证 Comp3 组件是否被缓存。我们可以观察到 Vue 开发者调试工具中的 Comp3 组件没有 inactive 的状态显示,因此 Comp3 组件没有被缓存,如下图所示。

图 6 通过Vue开发者调试工具验证Comp3组件是否被缓存
动画内置组件 Transition 与 TransitionGroup,配合动态加载组件 Component 与缓存组件 KeepAlive 可以实现动画效果。
以 Transition 组件为例,在项目根目录的 index.html 文件中添加下方代码,引入 animate.css 动画类库。
<link href="https://cdn.bootcdn.net/ajax/libs/animate.css/4.1.1/animate.min.css" rel="stylesheet">在 App 组件中,调用动画内置组件 Transition 实现动画效果的展现。App.vue 文件代码如下:
<template> <button @click="changeTab(Comp1)">ChangeComp1</button> <button @click="changeTab(Comp2)">ChangeComp2</button> <button @click="changeTab(Comp3)">ChangeComp3</button> <transition enter-active-class="animate__animated animate__tada" leave-active-class="animate__animated animate__bounceOutRight" > <keep-alive :include="['Comp1', 'Comp2']"> <component :is="tab"></component> </keep-alive> </transition> </template> <script setup> import Comp1 from './components/Comp1.vue'; import Comp2 from './components/Comp2.vue'; import Comp3 from './components/Comp3.vue'; import { ref, markRaw } from 'vue'; // 设置需要切换的组件,初始为Comp1,使用markRaw函数,不对组件进行递归响应式数据代理 const tab = ref(markRaw(Comp1)); // 定义切换组件函数,将组件本身当成参数传递 function changeTab(comp) { tab.value = markRaw(comp); } // 默认切换显示Comp1组件 changeTab(Comp1); </script>