首页 > 编程笔记 > JavaScript笔记 阅读:11

Vue KeepAlive组件用法详解(附带实例)

Vue 为动态加载的组件提供了一种性能优化方案——缓存组件,其利用内置组件 KeepAlive 实现。

组件在加载时会经历初始、挂载、更新、销毁生命周期,对于动态组件加载来说,频繁地切换组件会不断地重复组件的初始、挂载、销毁生命周期,这意味着程序需要不断地读取和释放内存,因此将会极大地影响内存开销,从而影响项目的性能。

如果可以将这样的组件暂存于内存中不做释放处理,使用的时候再从内存中取出,就可以省去组件创建和销毁等耗费内存的操作。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 两个属性用来解决这个困扰:
这两个属性值都可以设置为字符串、正则表达式与数组。

虽然 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>

相关文章