随着昨天最后一门考试结束,大三上学期的寒假也就正式开始了。打算继续完善我的SpringBooot + Vue的FakeShop网上书城项目,边学边做吧。
虽然目前看起来也是有模有样的,但是似乎并不是很符合一些实际的操作习惯,例如:由于商品列表采用瀑布式加载,当你从列表点击进入一个商品详情,再返回到商品列表时,你期望从上一次浏览到的位置开始,而不是整个页面重新加载一次,有什么办法可以直接缓存先前页面的状态呢?你可以尝试使用KeepAlive做到。
KeepAlive介绍和基本用法
KeepAlive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染 。也就是所谓的组件缓存
使用KeepAlive缓存页面(基础)
那么在实际情况中可以怎么使用KeepAlive呢?有常见的有一下几种方法。
1. 为路由添加meta属性
下面以需要缓存商城主页为例。 首先,你可以为要缓存页面的路由(在router/index.js中或router.js中)添加一个meta属性,并设置keepAlive
的值为true
。
{
path: '/',
name: '主页',
component: Home,
meta: {
keepAlive: true
}
}
然后,在app.js中,通过路由中是否存在keepAlive
的meta属性判断是否需要缓存页面,将RouterView的显示修改为:
2. 使用KeepAlive的include属性
keep-alive
标签提供了include
属性,用于指定需要缓存的页面,缓存的页面使用逗号(,)隔开。 在下面的例子中,读取存储在Vuex中的需要缓存的页面路由数组:
在Vuex中,使用数组列出需要进行缓存的页面路由:
state: {
keepAlive: [
"/",
"/searchResult",
"/page1",
"/page2",
"/page3"
]
}
动态使用KeepAlive缓存页面
如果你的项目存在从商品详情页面返回搜索结果页面需要进行缓存,而从其他页面返回搜索结果页面时不需要缓存,又该如何实现呢?
1. 采用为路由添加meta属性的方法
此时可以用到路由守卫中的beforeRouteLeave
方法,以搜索结果页跳转到商品详情页为例,将如下代码放置在搜索结果页的SearchResult.vue文件中(与data属性同级):
beforeRouteLeave(to, from, next) {
if (to.path == "/item") {
this.$route.meta.keepAlive = true;
} else {
//Do not keep alive
}
next();
}
至此,我们已经实现了搜索结果页面跳转到商品详情页面时的缓存,但当需要阻止Vue缓存页面或是清除缓存时又该怎么做呢?你可能会想到this.$route.meta.keepAlive = false
重新将KeepAlive设置为false
,这样看似解决了问题,当你尝试搜索其他关键词进入商品详情,再返回搜索结果也时,你会发现搜索结果页只显示第一次缓存的结果。为此,在文末将统一提供一种清除KeepAlive缓存的方式。
2. 采用使用KeepAlive的include属性
只需要在Vuex中编写mutation,动态向路由数组中添加项目即可;当需要删除缓存时从动态数组中移除指定项目即可。这样看似可以解决问题,但是当你使用Vue devtools查看时会发现页面/组件仍然存在,只是被设置为inactive状态。当一段时间的操作后,后台将存在更多的缓存页面但不会被删除。
清空缓存的正确方法
经过一段时间地翻阅资料,发现已经有人通过操控keep-alive组件里的cahce列表,实现移除缓存的操作。将其封装成一个方法,你可以放置在App.vue的methods中:
clearCache(obj) {
if (obj.$vnode && obj.$vnode.data.keepAlive) {
if (obj.$vnode.parent && obj.$vnode.parent.componentInstance && obj.$vnode.parent.componentInstance.cache) {
if (obj.$vnode.componentOptions) {
var key = obj.$vnode.key == null? obj.$vnode.componentOptions.Ctor.cid +(obj.$vnode.componentOptions.tag? `::${obj.$vnode.componentOptions.tag}`: ""): obj.$vnode.key;
var cache = obj.$vnode.parent.componentInstance.cache;
var keys = obj.$vnode.parent.componentInstance.keys;
if (cache[key]) {
if (keys.length) {
var index = keys.indexOf(key);
if (index > -1) {
keys.splice(index, 1);
}
}
delete cache[key];
}
}
}
}
obj.$destroy();
}
当其他页面需要调用这个方法时,可以通过this.$parent.clearCache(this);
进行调用,因此只在从商品详情页面返回搜索结果页面才进行缓存,其他页面进入搜索结果页面时不进行缓存的具体代码可以写成:
beforeRouteLeave(to, from, next) {
if (to.path == "/item") {
this.$route.meta.keepAlive = true;
} else {
this.$parent.clearCache(this);
}
next();
}
即,将在离开搜索结果页面时判断到达页面是否为商品详情页面,是则缓存搜索结果页面;否则清除页面的缓存,避免只显示第一次缓存的结果。