盒子
盒子
文章目录
  1. 案例
  2. 小结

看keep-alive如何在项目中失效

banner

keep-alive 是vue内置的一个组件,其作用是缓存不活动的组件。众所周知,一般在组件进行切换时,默认会进行销毁。若想在组件切换时保存原有的状态,那就需要利用 keep-alive 来实现。

keep-alive 的用法也不复杂,官网也做了说明,并给了示例。但有时规规矩矩的用了就是不生效。相信大家也会有这样的烦恼,接下来小编就列举下容易造成 keep-alive 失效的几个点。

案例

  1. 关键词没拿捏好,一行注释都能让你忙活一上午。

<keep-alive> 是用在其一个直属的子组件被切换的情形。“一个”、“直属”这两点很好理解,却容易被忽视。有时为了提高代码可读性,我们会做个注释。而这个注释位置要是不对,违反了“一个”原则,就能造成 keep-alive 失效。

1
2
3
4
5
<!--掉坑,失效-->
<keep-alive>
<!-- 注释也会是keep-alive失效,删除本条注释功能正常 -->
<component :is="view"></component>
</keep-alive>

关于“直属”,或许你能拍胸脯告诉我你不可能因为这点翻车。我承认在刚开始开发项目时,开发者都头脑清晰,加上有官方文档的辅助能很好的规避掉错误点。但随着项目战线拉长,快速的需求变更迭代,难保你在实现功能过程中会不小心掉入了“直属”的坑中。

1
2
3
4
5
6
<!--掉坑,失效-->
<keep-alive>
<div style="margin: 10px;">
<component :is="view"></component>
</div>
</keep-alive>
  1. <keep-alive> 直属子组件中使用了 v-for
1
2
3
4
5
6
7
8
9
10
<!--失效-->
<keep-alive>
<template v-for="tab in tabs" :key="tab">
<component :is="`theme${tab}`" v-if="tab === curTab"/>
</template>
</keep-alive>
<!--正常-->
<keep-alive>
<component :is="`theme${curTab}`"/>
</keep-alive>
  1. 你给我的,却不是我想要的。

这里就涉及到 keep-alive 的俩 prop(include 和 exclude)。简单介绍下这俩 prop 的类型:

  • include - string | RegExp | Array。只有名称匹配的组件会被缓存。
  • exclude - string | RegExp | Array。任何名称匹配的组件都不会被缓存。

『你给我的,却不是我想要的』大概有这三种情形:

  • includeexclude)设置正确,使用了动态组件但并未在页面中引入相关组件;
  • 找错对象了,易发在 keep-alive 结合 router-view 使用的场景,使用了 vue-router的 name 属性来设置includeexclude);
  • includeexclude)给的值与组件自身的 name 或是局部注册名称不匹配,定义时是小驼峰写法,使用时是大驼峰写法;

这三点中最容易犯错的就是第二点,所以重要的事情要强调下。

vue-router 的 name 属性是用于路由跳转!!!

  1. 用逗号分隔字符串来表示 includeexclude时,因多余空格导致失效。

为防止这种错误发生,推荐使用另外两种方式表示。正则表达式或一个数组。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

<!--逗号分隔字符串 失效-->
<keep-alive include="Home, About">
<component :is="view"></component>
</keep-alive>

<!--逗号分隔字符串 正常-->
<keep-alive include="Home,About">
<component :is="view"></component>
</keep-alive>

<!-- 使用正则表达式 -->
<keep-alive :include="/Home|About/">
<component :is="view"></component>
</keep-alive>

<!-- 使用数组 -->
<keep-alive :include="['Home', 'About']">
<component :is="view"></component>
</keep-alive>

  1. Vue3 中局部注册匿名组件后,缓存失效。

include 和 exclude 匹配首先检查组件自身的 name 选项,如果 name 选项不可用,则匹配它的局部注册名称 (父组件 components 选项的键值)。

按照官方文档的意思,只有组件自身 name 不可用(我的理解是就未指定 name),才匹配它的局部注册名称。这里我在Vue2 和 Vue3都做了试验,发现两者表现不同。

Vue2写法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<template>
<div id="app">
<button @click="switchTheme">切换主题</button>
<h4>直属的子组件切换,子组件被缓存</h4>
<keep-alive :include="['a1', 'a2']">
<component :is="`a${curTab}`"/>
</keep-alive>
</div>
</template>
<script>
const Theme1 = {
// name: 'Theme1',
template: `<div>主题1【{{ date }}</div>`,
data() {
return {
date: new Date()
}
}
}
const Theme2 = {
// name: 'Theme2',
template: `<div>主题2【{{ date }}</div>`,
data() {
return {
date: new Date()
}
}
}
export default {
components: { 'a1': Theme1, 'a2': Theme2 },
data() {
return {
curTab: 2,
};
},
methods: {
switchTheme: function() {
this.curTab += 1
if (this.curTab > 2) {
this.curTab = 1
}
}
}
};
</script>

Video_2022-05-13_150716.gif

点击查看效果

Vue3写法,相同部分的代码已省略

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<template>
<!--同上-->
</template>
<script>
import { ref } from 'vue'

const Theme1 = {} // 同上
const Theme2 = {} // 同上

export default {
components: { 'a1': Theme1, 'a2': Theme2 },
setup() {
const curTab = ref(2)
const switchTheme = () => {
curTab.value += 1
if (curTab.value > 2) {
curTab.value = 1
}
}
return {
curTab,
switchTheme
}
}
};
</script>

2.gif

点击查看效果

通过对比我们可以发现除了因 Vue 版本造成的写法不同外,二者并无差异。但运行后表现出在 Vue2 中keep-alive 功能正常,在 Vue3 中却失效。要是你的项目是用 Vue3 开发的,你就老老实实的给组件指定 name 吧。

  1. 匿名组件造成 keep-alive 失效。

匿名组件就是未指定 name 的组件,但你要是通过局部(全局)组件注册后,它就是具名组件了。项目中容易出现匿名组件的就是Vue页面,keep-alive 结合 router-view 使用就容易让你困在vue-router 的 name 属性中,切记这个 name 属性只用于路由跳转。

vue2写法:

1
2
3
<keep-alive>
<router-view />
</keep-alive>

vue3写法:

1
2
3
4
5
6
7
<router-view v-slot="{ Component }">
<transition>
<keep-alive>
<component :is="Component" />
</keep-alive>
</transition>
</router-view>
  1. 多级路由导致缓存失效

多级路由一般出现在功能繁杂的后台管理系统中,为提高系统性能和改善用户体验,产品可能会要求对页面进行缓存以达到快速地在页面间来回切换。keep-alive 进而在管理系统中崭露头角,但其表现极其不稳定,着实让人摸不着头脑。

Layout.vue

1
2
3
4
5
6
7
8
9
<template>
<router-view v-slot="{ Component }">
<transition mode="out-in" name="fade-transform">
<keep-alive :include="['theme1', 'theme2']">
<component :is="Component"/>
</keep-alive>
</transition>
</router-view>
</template>

NestRouterView.vue

1
2
3
<template>
<router-view />
</template>

router.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const routes = [
{
path: '/',
component: () => import('@/views/Layout.vue'),
children: [
{
name: 'theme',
path: '',
component: () => import('@/views/NestRouterView.vue'),
children: [
{
name: 'theme1',
path: 'theme/1',
component: () => import('@/views/Theme1.vue'),
meta: {
title: '主题1',
keepAlive: true,
},
},
]
}
]
}
]

这里多级路由缓存失效的原因是借助了『空组件』(NestRouterView.vue)。关于缓存的设置也仅限生效于二级路由中,三级路由中并未涉及到 keep-alive,也就无从谈及失效一说。如何解决多级路由缓存失效问题,请移步小编的另一篇文章vue多级路由缓存(keep-alive)失效的解决方案

  1. <keep-alive> 不会在函数式组件中正常工作,因为它们没有缓存实例。

函数式组件只是函数,无状态 (没有响应式数据),也无实例 (在vue2中,没有 this 上下文)。

以上是目前小编所知的8种失效场景。只有规避掉失效点,才能真正用好 keep-alive

小结

  • include 和 exclude 推荐使用正则表达式或一个数组来表示;
  • 需要缓存的组件都写 name 属性,杜绝匿名组件;
  • 认清 keep-alivevue-router的关系,杜绝乱用 name
  • 面对多层 router-view 嵌套,找准 keep-alive 位置;

本文作者: mileOfSunshine
本文链接: https://mileofsunshine.github.io/2022/05/11/2022-05-11-how-to-use-keep-alive/
版权声明:文章是原创作品。转载请注明出处!