Vue学习篇之KeepAlive缓存和删除缓存

Vue学习篇之KeepAlive缓存和删除缓存

随着昨天最后一门考试结束,大三上学期的寒假也就正式开始了。打算继续完善我的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();
}

即,将在离开搜索结果页面时判断到达页面是否为商品详情页面,是则缓存搜索结果页面;否则清除页面的缓存,避免只显示第一次缓存的结果。

参考文章

发布者

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注