需求1:
pageA
->
pageB
->
pageC
缓存pageB
:
从pageA
进入pageB
,刷新页面并缓存页面;
从pageC
返回pageB
,不刷新页面
- 在 vuex(或其他存储方案) 声明两个数组
{
state: () => ({
keepAliveViews: [],
notAliveViews: [],
}),
mutations: {
/**
* 记录需要缓存的路由视图
*/
saveKeepAliveViews(state, { keepAliveViews }) {
state.keepAliveViews = keepAliveViews;
},
/**
* 清除页面缓存
*/
clearCacheView(state, { notAliveViews }) {
state.notAliveViews = notAliveViews;
},
},
}
- 在定义路由时,在
meta
中添加属性keepAlive:true
{
name: "pageB",
path: "page-b",
component: () => import("***/pageB.vue"),
meta: {
keepAlive: true,
},
},
- 在适当位置遍历路由表,记录需要缓存的路由的组件名称
// 记录需缓存的路由/组件
const keepAliveViews = [];
router.getRoutes().forEach((routeItem) => {
if (routeItem?.meta?.keepAlive) {
// 组件name和路由name保持一致, 所以可以直接使用routeItem.name
// 也可以在 meta 中添加属性 compName 来用,或其他方案
keepAliveViews.push(routeItem.name);
}
});
store.commit("saveKeepAliveViews", { keepAliveViews });
- 在路由根组件中
// template
<router-view v-slot="{ Component }">
<keep-alive :include="keepAliveViews" :exclude="notAliveViews">
<component :is="Component" />
</keep-alive>
</router-view>
// js
const keepAliveViews = computed(() => store.state.keepAliveViews);
const notAliveViews = computed(() => store.state.notAliveViews);
keep-alive
会缓存include
中存在的组件,会清除exclude
中的组件缓存;
- 手动清除组件缓存
/**
* 清除路由(组件/页面)缓存
*/
export function clearCacheView(destroyCompNames) {
store.commit("clearCacheView", { notAliveViews: destroyCompNames });
// 清除缓存后,要重置数组为空,下次才能再次缓存
// 实际上不知道什么时候会完成缓存的清除,这里取500ms,一般满足需求
setTimeout(() => {
store.commit("clearCacheView", { notAliveViews: [] });
}, 500);
}
{
name: "pageA",
path: "page-a",
component: () => import("***/pageA.vue"),
meta: {},
beforeEnter: () => {
clearCacheView(["pageB"]); // 这里的"pageB"是页面pageB的组件名称
},
返回pageA
时,手动清除pageB
的缓存;
需求2:
使用缓存页面,大多数都是列表页进入详情页,所以还需要考虑列表页的滚动位置的问题.
即:pageC
返回pageB
时,pageB
要保持在离开时的位置
- 在 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;
}
},
},
}
- 在定义路由时,在
meta
中添加属性scrollEls
{
name: "pageB",
path: "page-b",
component: () => import("***/pageB.vue"),
meta: {
keepAlive: true,
scrollEls: [".scroll-list"], // 数组形式,可添加多个可滚动元素
},
},
- 在路由守卫中
/**
* 全局前置守卫
*/
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;
item2.top = 0;
}
});
// 使用后重置滚动位置为0
store.commit("setkeepAliveViewsScrollPostion", item);
}
});
});
网友评论