vue缓存功能实现
大约 3 分钟
vue缓存功能实现
参考:https://www.jianshu.com/p/62cbcac077aa
主要针对的是在移动端,需要支持缓存、手动刷新、自动刷新
- 添加界面保存后,需要手动刷新列表页面
- 返回一级页面,需要清除缓存,防止第二次在进入数据一直不重新加载
需求1:
pageA->pageB->pageC缓存pageB: 从pageA进入pageB,刷新页面并缓存页面; 从pageC返回pageB,不刷新页面需求2: 使用缓存页面,大多数都是列表页进入详情页,所以还需要考虑列表页的滚动位置的问题. 即:
pageC返回pageB时,pageB要保持在离开时的位置
需求1
pageA->pageB->pageC场景:首页需要清除全部缓存,列表页面进入详情需要缓存详情界面
pageA: 首页(清除缓存的页面,不然缓存的页面一直不能刷新,显然不对)
pageB:列表页面(默认是需要进行缓存的,从详情界面返回来不应该重新刷新页面)
pageC: 详情界面
1. 在 vuex(或其他存储方案) 声明两个数组
{
state: () => ({
keepAliveViews: [],
notAliveViews: [],
}),
mutations: {
/**
* 记录需要缓存的路由视图
*/
saveKeepAliveViews(state, { keepAliveViews }) {
state.keepAliveViews = keepAliveViews;
},
/**
* 清除页面缓存
*/
clearCacheView(state, { notAliveViews }) {
state.notAliveViews = notAliveViews;
},
},
}
2. 在定义路由时,在meta中添加属性keepAlive:true
{
name: "pageB",
path: "page-b",
component: () => import("***/pageB.vue"),
meta: {
keepAlive: true,
},
},
3. 在适当位置遍历路由表,记录需要缓存的路由的组件名称
可以写在app.vue组件下
注意: 需要注意的是 组件name和路由name保持一致 否则可能缓存不生效
// 记录需缓存的路由/组件
const keepAliveViews = [];
router.getRoutes().forEach((routeItem) => {
if (routeItem?.meta?.keepAlive) {
// 组件name和路由name保持一致, 所以可以直接使用routeItem.name
// 也可以在 meta 中添加属性 compName 来用,或其他方案
keepAliveViews.push(routeItem.name);
}
});
store.commit("saveKeepAliveViews", { keepAliveViews });
4. 在路由根组件中
keep-alive会缓存include中存在的组件,会清除exclude中的组件缓存;
// template
<router-view v-slot="{ Component }">
<keep-alive :include="keepAliveViews" :exclude="notAliveViews">
<component :is="Component" />
</keep-alive>
</router-view>
computed: {
getKeepAliveViews(){
return this.$store.state.keepAliveViews;
},
getNotAliveViews(){
return this.$store.state.notAliveViews
}
}
5. 清除缓存
清除缓存共2处,
- 一是在路由设置清除缓存实现自动清除
- 二是手动清除,添加界面涉及到业务保存需要手动清除缓存
5.1 在路由js中
// 清除缓存的组件
export function clearCacheView(destroyCompNames=[]) {
store.dispatch("setNotAliveViews", destroyCompNames);
console.log("notAliveViews:" + JSON.stringify(store.state.notAliveViews));
// 清除缓存后,要重置数组为空,下次才能再次缓存
// 实际上不知道什么时候会完成缓存的清除,这里取500ms,一般满足需求
setTimeout(() => {
var notAliveViews = [];
store.dispatch("setNotAliveViews", notAliveViews);
}, 500);
}
返回pageA时,手动清除pageB的缓存;
{
name: "pageA",
path: "page-a",
component: () => import("***/pageA.vue"),
meta: {},
beforeEnter: () => {
clearCacheView(["pageB"]); // 这里的"pageB"是页面pageB的组件名称
},
5.2 手动调用清除缓存
在main.js 中
import {clearCacheView} from "./router";
myApp.config.globalProperties.$clearCacheView = clearCacheView;
保存方法中调用清除
save(){
// 业务保存
Toast('操作成功')
this.$clearCacheView([ 'pageB' ]);
this.$router.back();
}
需求2
缓存页面记录滚动条位置
1. 在 vuex(或其他存储方案) 声明一个数组
{
state() {
return {
keepAliveViewsScrollPostion: [],
};
},
mutations: {
// 设置缓存页面滚动元素的位置
setkeepAliveViewsScrollPostion(state, { routeName, list }) {
const item = state.keepAliveViewsScrollPostion.find((t) => t.routeName === routeName);
if (!item) {
state.keepAliveViewsScrollPostion.push({ routeName, list });
} else {
item.list = list;
}
},
},
}
2. 在定义路由时,在meta中添加属性scrollEls
特别说明需要保证 scrollEls 指定要的元素需要
overflow-y: auto;
{
name: "pageB",
path: "page-b",
component: () => import("***/pageB.vue"),
meta: {
keepAlive: true,
scrollEls: [".scroll-list"], // 数组形式,可添加多个可滚动元素
},
},
3. 在路由守卫中
/**
* 全局前置守卫
*/
router.beforeEach((to, from) => {
// 缓存页面:记录滚动位置
if (from.meta.scrollEls) {
const scrollObj: any = { routeName: from.name, list: [] };
from.meta.scrollEls.forEach((element) => {
const el = document.querySelector(element);
if (el) {
scrollObj.list.push({
el: element,
top: el.scrollTop,
});
}
});
store.commit("setkeepAliveViewsScrollPostion", scrollObj);
}
});
/**
* 全局后置钩子
*/
router.afterEach((to, from) => {
// 缓存页面:滚动到指定位置
nextTick(() => {
if (to.meta.scrollEls) {
const item = store.state.keepAliveViewsScrollPostion.find((t) => t.routeName === to.name);
if (!item) return;
item.list.forEach((item2) => {
const el = document.querySelector(item2.el);
if (el) {
el.scrollTop = item2.top;
}
});
}
});
});