keep-alive 是vue内置的一个组件,其作用是缓存不活动的组件。众所周知,一般在组件进行切换时,默认会进行销毁。若想在组件切换时保存原有的状态,那就需要利用 keep-alive 来实现。
keep-alive 的用法也不复杂,官网也做了说明,并给了示例。但有时规规矩矩的用了就是不生效。相信大家也会有这样的烦恼,接下来小编就列举下容易造成 keep-alive 失效的几个点。
案例
- 关键词没拿捏好,一行注释都能让你忙活一上午。
<keep-alive>
是用在其一个直属的子组件被切换的情形。“一个”、“直属”这两点很好理解,却容易被忽视。有时为了提高代码可读性,我们会做个注释。而这个注释位置要是不对,违反了“一个”原则,就能造成 keep-alive
失效。
<!--掉坑,失效-->
<keep-alive>
<!-- 注释也会是keep-alive失效,删除本条注释功能正常 -->
<component :is="view"></component>
</keep-alive>
关于“直属”,或许你能拍胸脯告诉我你不可能因为这点翻车。我承认在刚开始开发项目时,开发者都头脑清晰,加上有官方文档的辅助能很好的规避掉错误点。但随着项目战线拉长,快速的需求变更迭代,难保你在实现功能过程中会不小心掉入了“直属”的坑中。
<!--掉坑,失效-->
<keep-alive>
<div style="margin: 10px;">
<component :is="view"></component>
</div>
</keep-alive>
<keep-alive>
直属子组件中使用了v-for
。
<!--失效-->
<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>
- 你给我的,却不是我想要的。
这里就涉及到 keep-alive 的俩 prop(include
和 exclude
)。简单介绍下这俩 prop 的类型:
include
-string | RegExp | Array
。只有名称匹配的组件会被缓存。exclude
-string | RegExp | Array
。任何名称匹配的组件都不会被缓存。
『你给我的,却不是我想要的』大概有这三种情形:
include
(exclude
)设置正确,使用了动态组件但并未在页面中引入相关组件;- 找错对象了,易发在
keep-alive
结合router-view
使用的场景,使用了vue-router
的 name 属性来设置include
(exclude
); include
(exclude
)给的值与组件自身的name
或是局部注册名称不匹配,定义时是小驼峰写法,使用时是大驼峰写法;
这三点中最容易犯错的就是第二点,所以重要的事情要强调下。
vue-router 的 name 属性是用于路由跳转!!!
- 用逗号分隔字符串来表示
include
和exclude
时,因多余空格导致失效。
为防止这种错误发生,推荐使用另外两种方式表示。正则表达式或一个数组。
<!--逗号分隔字符串 失效-->
<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>
- 在
Vue3
中局部注册匿名组件后,缓存失效。
include
和exclude
匹配首先检查组件自身的 name 选项,如果 name 选项不可用,则匹配它的局部注册名称 (父组件 components 选项的键值)。
按照官方文档的意思,只有组件自身 name 不可用(我的理解是就未指定 name),才匹配它的局部注册名称。这里我在Vue2 和 Vue3都做了试验,发现两者表现不同。
Vue2写法:
<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>
Vue3写法,相同部分的代码已省略
<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>
通过对比我们可以发现除了因 Vue 版本造成的写法不同外,二者并无差异。但运行后表现出在 Vue2 中keep-alive
功能正常,在 Vue3 中却失效。要是你的项目是用 Vue3 开发的,你就老老实实的给组件指定 name 吧。
- 匿名组件造成
keep-alive
失效。
匿名组件就是未指定 name 的组件,但你要是通过局部(全局)组件注册后,它就是具名组件了。项目中容易出现匿名组件的就是Vue页面,keep-alive
结合 router-view
使用就容易让你困在vue-router 的 name 属性中,切记这个 name 属性只用于路由跳转。
vue2写法:
<keep-alive>
<router-view />
</keep-alive>
vue3写法:
<router-view v-slot="{ Component }">
<transition>
<keep-alive>
<component :is="Component" />
</keep-alive>
</transition>
</router-view>
- 多级路由导致缓存失效
多级路由一般出现在功能繁杂的后台管理系统中,为提高系统性能和改善用户体验,产品可能会要求对页面进行缓存以达到快速地在页面间来回切换。keep-alive 进而在管理系统中崭露头角,但其表现极其不稳定,着实让人摸不着头脑。
Layout.vue
<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
<template>
<router-view />
</template>
router.js
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)失效的解决方案。
<keep-alive>
不会在函数式组件中正常工作,因为它们没有缓存实例。
函数式组件只是函数,无状态 (没有响应式数据),也无实例 (在vue2中,没有 this
上下文)。
以上是目前小编所知的8种失效场景。只有规避掉失效点,才能真正用好 keep-alive
。
小结
include
和exclude
推荐使用正则表达式或一个数组来表示;- 需要缓存的组件都写
name
属性,杜绝匿名组件; - 认清
keep-alive
和vue-router
的关系,杜绝乱用name
; - 面对多层
router-view
嵌套,找准keep-alive
位置;
本文首发掘金平台,来源个人博客