如何在 Vue3 项目中使用 keep-alive 组件

为什么要写这篇文章?

最近在学习 Vue3 的使用,在使用 vue-router 插件时,想到组件缓存的问题。我想要实现的效果是,搭配 vue-router, 定义路由表时,在 meta 中添加 keepAlive 变量,并通过 route.meta.keepAlive 这一变量控制页面组件的缓存与否,这也是使用 Vue2 时的缓存思路。

在使用 <keep-alive> 时遇到了一些问题。

我首先习惯性地尝试使用 Vue2 的方式使用 <keep-alive> 处理需要缓存的组件 ,但 Vue3 中,<keep-alive> 的使用方式与 Vue2 有所不同,Vue2 的使用方式并不能实现组件缓存。

1
2
3
4
5
6
7
// App.vue
<template>
<keep-alive>
<router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive"></router-view>
</template>

查阅网上的一些文章,对 Vue3 的 <keep-alive> 使用与 Vue2 不同这一点基本都有提及,但代码大多都是这种:

1
2
3
4
5
6
7
8
9
// App.vue
<template>
<router-view v-slot="{ Component }">
<keep-alive>
<component :is="Component" v-if="$route.meta.keepAlive"></component>
</keep-alive>
<component :is="Component" v-if="!$route.meta.keepAlive"></component>
</router-view>
</template>

这种方式在某些情况下的组件渲染有问题:页面 A 非缓存,页面 B、C 缓存,A=>B=>A=>C 时,A 和 C 会一起被渲染。

还有这种:

1
2
3
4
5
6
7
8
// App.vue
<template>
<router-view v-slot="{ Component }">
<keep-alive include="A,C">
<component :is="Component"></component>
</keep-alive>
</router-view>
</template>

这种方式虽然缓存效果得以实现,但放弃了使用 keepAlive 属性,与我们想要实现的逻辑实在是南辕北辙。

多番思虑后,终于从 vue-router 的官方文档中找到解决方案,故写了这边文章用以记录。

<keep-alive> 组件简介

<keep-alive> 的作用,熟悉 Vue2 的同学必然不会陌生,这里也就不再赘述。

值得一说的是,<keep-alive> 有几个需要注意的点,列在下面:

  1. <keep-alive> 要求同时只有一个子元素被渲染;
  2. <keep-alive> 是用在其一个直属的子组件被切换的情形,如果你在其中有 v-for 则不会工作;
  3. 属性 includeexclude 允许组件有条件地缓存,其匹配首先检查组件自身的 name 选项,如果 name 选项不可用,则匹配它的局部注册名称 (父组件 components 选项的键值)。匿名组件不能被匹配
  4. <keep-alive> 不会在函数式组件中正常工作,因为它们没有缓存实例。

Vue2 的使用方式

1
2
3
4
5
6
7
// App.vue
<template>
<keep-alive>
<router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive"></router-view>
</template>

Vue3 的使用方式

注意 key 的使用,解决上述方法 2的问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<template>
<router-view v-slot="{ Component, route }">
<keep-alive>
<component
v-if="route.meta.keepAlive"
:is="Component"
:key="route.path"
></component>
</keep-alive>
<component
v-if="!route.meta.keepAlive"
:is="Component"
:key="route.path"
></component>
</router-view>
</template>