一、需求
产品要求:Vue移动端项目进入列表页,列表页需要刷新,而从详情页返回列表页,列表页则需要缓存并且还原页面滚动条位置
二、实现思路
1、使用Vue中的keep-alive组件,keep-alive提供了路由缓存功能
2、因为我项目只是针对某几个列表页面做缓存,我就直接把指定的几个页面单独的做处理(即:把需要做缓存的页面路由的meta新增了keepAlive属性,当keepAlive为true时缓存,为false则不缓存),从而实现进入列表页,列表页需要刷新,而从详情页返回列表页,列表页则需要保持页面缓存
建议使用
keep-alive
的includes
属性来做缓存页面
三、最终效果
四、具体实现
1、app.vue文件修改
<template>
<div id="app">
<keep-alive>
<router-view class="Router" v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
<router-view class="Router" v-if="!$route.meta.keepAlive"></router-view>
</div>
</template>
2、在动态路由生成后初始化指定缓存页面路由设置meta
中keepAlive
属性为true
3、单个列表页面的缓存处理(详情返回到列表滚动条的位置)
beforeRouteLeave(to, from, next) {
// console.log('777---', from)
this.scroll = document.querySelector('.endInfo').scrollTop
// 离开页面时,需要清除缓存(为了下次进入后刷新页面)
from.meta.keepAlive = false
next()
},
activated() {
// 注意`endInfo`类是:列表box的顶级类,用来计算滚动条的距离
document.querySelector('.endInfo').scrollTop = this.scroll
console.log('缓存页面距离', this.scroll)
},
4、从详情页面返回到列表,需要如下设置(关键步骤)
beforeRouteLeave(to, from, next) {
console.log('支付单详情页', to)
// 设置下一个路由的meta,让列表页面缓存,即不刷新(即:此详情页面返回到sell和customerMangement页面后此页面缓存)
if (to?.path?.includes('sell') || to?.name?.includes('customerMangement')) {
to.meta.keepAlive = true
}
next()
},
五、缺陷:按此方法缓存页面,会出现列表新增数据后进入详情在返回到列表时,之前新增的数据没有(即还是上一次的缓存列表数据)
六、解决方法:在离开列表页面就手动清除keep-alive缓存
1、在app.vue页面加上clearKeepAlive方法(并使用EventBus全局监听)
解释:
this.$bus
就是在main.js
加上:Vue.prototype.$bus = new Vue()
<template>
<div id="app">
<keep-alive>
<router-view class="Router" v-if="$route.meta.keepAlive" :key="fullPath"></router-view>
</keep-alive>
<router-view class="Router" v-if="!$route.meta.keepAlive"></router-view>
</div>
</template>
<script>
export default {
name: 'app',
computed: {
fullPath() {
// console.log(this.$route.fullPath);
return this.$route.fullPath;
},
},
mounted() {
console.log('app---mounted')
// 注册监听全局的clearKeepAlive方法,可在其他组件中触发此方法
this.$bus.$on("clearKeepAlive", this.clearKeepAlive);
},
methods: {
// 根据fullUrl清除keepAlive
clearKeepAlive(fullUrl) {
// console.log('bus触发要清除的keepAlive', fullUrl);
this.$children.forEach((item) => {
if (item.$vnode.data.key == fullUrl) {
// console.log('destorykeepAlive', item.$vnode.data.key, fullUrl, item);
this.destorykeepAlive(item);
}
});
},
// 封装清除某个组件的keepAlive状态,并销毁
destorykeepAlive(keepAliveDom) {
if (keepAliveDom?.$vnode?.data?.keepAlive) {
if (keepAliveDom?.$vnode?.parent?.componentInstance?.cache) {
if (keepAliveDom.$vnode.componentOptions) {
var key =
keepAliveDom.$vnode.key == null
? keepAliveDom.$vnode.componentOptions.Ctor.cid +
(keepAliveDom.$vnode.componentOptions.tag
? `::${keepAliveDom.$vnode.componentOptions.tag}`
: "")
: keepAliveDom.$vnode.key;
var cache =
keepAliveDom.$vnode.parent.componentInstance.cache;
var keys = keepAliveDom.$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];
}
}
}
}
keepAliveDom.$destroy();
}
}
</script>
2、单个列表页面使用
beforeRouteLeave(to, from, next) {
if (to.name !== '列表进入的详情页面name') {
// 离开列表页面的时候:当不是进入列表详情页面时清除列表页面的缓存
this.$bus.$emit("clearKeepAlive", from.path)
}
next()
},
相关文章
基于ElementUi再次封装基础组件文档
基于ant-design-vue再次封装基础组件文档
vue3+ts基于Element-plus再次封装基础组件文档