背景介紹
在前端日常項目開發(fā)中,大家應該都有遇到過這樣的需求:當我們在列表頁輸入條件進行數(shù)據(jù)篩選時,我們可能需要根據(jù)篩選后的結(jié)果進入到某一條的詳情頁,當我們看完詳情頁內(nèi)容,再返回列表頁,此時我們希望還是之前的篩選結(jié)果,包括條件和分頁等。
在實際開發(fā)中,改變頁面導航的路由都會重新匹配頁面,初始化頁面的數(shù)據(jù)狀態(tài),無法直接達到這種效果。但Vue
給我們提供了“秘密武器”-keep-alive
,可以實現(xiàn)頁面的數(shù)據(jù)緩存。
其實以前也寫過類似的實現(xiàn)頁面緩存的方案:Vue-Router 實現(xiàn)前端頁面緩存,是通過keep-alive
的include
屬性控制要緩存的頁面。但是這種方式不夠靈活,一旦緩存了頁面,從其他頁面(非詳情頁面)跳入到列表頁時數(shù)據(jù)也不會重新更新,需要在組件路由守衛(wèi)beforeRouteEnter
中去做判斷處理,十分麻煩。
因此,經(jīng)過參考探索,今天來分享一個一勞永逸的實現(xiàn)頁面緩存的方案。
實現(xiàn)思路
總體思路:
只要是列表頁(需要使用緩存的頁面),進入之前,都將其添加到要緩存的頁面中,離開列表頁時判斷新(to)路由是否是該列表頁指定的詳情頁(配置路由時通過 cacheTo 字段來指定),如果不是就清空緩存。
思路分析
根據(jù)總體思路,主要分為以下四種情況:
-
from
和to
都不是列表頁- 都不需要緩存,清空以前的緩存
-
from
和to
都是列表頁- 2.1-如果
to
不在from
的配置中,清空緩存,新增to
緩存 - 2.2-如果
to
在from
的配置中,保留from
緩存,新增to
緩存
- 2.1-如果
-
to
是列表頁,from
不是- 3.1-
from
不在to
的配置中,清空緩存,新增to
緩存 - 3.2-
from
在to
的配置中,不做處理(因為 3.1 已經(jīng)緩存了 to)
- 3.1-
-
from
是列表頁,to
不是- 4.1-
to
不在from
的配置中,清空緩存 - 4.2-
to
在from
的配置中,不做處理(因為 from 已經(jīng)在 3.1 時緩存)
- 4.1-
以上思路就是日常所有的場景,理解起來可能會比較繞,結(jié)合實際場景理解就會發(fā)現(xiàn)所有情況都已經(jīng)覆蓋到。
具體實現(xiàn)
前置須知
- 在路由表配置中維護一個
cacheTo
字段,如果配置了該字段就表示該路由為列表頁。 - 控制緩存的邏輯,我們單獨在一個文件內(nèi)完成,作為組件單獨管理。為了避免
include
的管理更加簡單,就不適用vuex
,這里使用Vue.observable
。 - 為了避免維護
keep-alive
的include
屬性對代碼造成侵入,這里使用函數(shù)式組件。 - 為了在管理緩存的組件內(nèi)能獲取到
to
和from
,需要在組件內(nèi)注冊一個beforeEach
鉤子,vue-router
的beforeEach
鉤子是可以重復注冊的,按照注冊順序執(zhí)行。
實現(xiàn)細節(jié)
// keep-alive-rorute.js
import Vue from "vue";
/**
* 創(chuàng)建存儲緩存?zhèn)}庫
*/
// 存儲需要緩存頁面路由
const cacheState = Vue.observable({
caches: [],
});
// 清理路由緩存
const clearCache = () => {
if (!cacheState.caches.length) return;
cacheState.caches = [];
};
// 新增路由緩存
const addCache = (name) => cacheState.caches.push(name);
const defaultHook = (to, from, next) => next();
// 路由進入鉤子函數(shù)
export const beforeRouterEachHook = (hook = defaultHook) => {
return (to, from, next) => {
/**
* 思路:只要是類列表頁,進入之前都將其緩存,離開時判斷新路由是否是該類列表頁指定的類詳情頁,如果不是就清除緩存。
*/
// to頁面路由
const toRouteName = to.name;
// to頁面為列表頁時配置的to路由集合
const toCacheRoutes = (to.meta || {}).cacheTo;
// to頁面是否是列表頁
const isToPageList = toCacheRoutes && toCacheRoutes.length;
// from頁面路由名稱
const fromRouteName = from.name;
// form頁面為列表頁時,配置的to頁面路由集合
const fromCacheRoutes = (from.meta || {}).cacheTo;
// from頁面是否是列表頁
const isFromPageList = fromCacheRoutes && fromCacheRoutes.length;
// 1如果to,from都不是列表頁,清除所有緩存
if (!isToPageList && !isFromPageList) {
clearCache();
} else if (isToPageList && isFromPageList) {
// 2如果to,from都是列表頁
// 如果to列表頁在from列表頁的配置中就要緩存,否則不用
if (fromCacheRoutes.indexOf(toRouteName) < 0) {
clearCache();
}
if (to.matched && to.matched.length > 2) {
// 三級路由時,緩存父子兩級路由
to.matched.map((element) => {
if (element.name) {
addCache(element.name);
}
});
} else {
// 二級路由緩存當前路由
addCache(toRouteName);
}
} else if (isToPageList) {
// 3如果to是列表頁
// 如果from不在to的配置中,清除所有緩存,同時緩存新路由
if (toCacheRoutes.indexOf(fromRouteName) < 0) {
clearCache();
if (to.matched && to.matched.length > 2) {
// 三級路由時緩存父子兩級路由
to.matched.map((element) => {
if (element.name) {
addCache(element.name);
}
});
} else {
// 二級路由緩存當前路由
addCache(toRouteName);
}
}
} else if (isFromPageList) {
// 4如果from頁面是列表頁
// to不在from的配置中清空緩存
if (fromCacheRoutes.indexOf(toRouteName) < 0) {
clearCache();
}
}
return hook(to, from, next);
};
};
// 緩存路由組件
export const KeepAliveRoute = {
install(Vue) {
const component = {
name: "KeepAliveRoute",
functional: true,
render(h, params) {
return h(
"keep-alive",
{ props: { include: cacheState.caches } },
params.children
);
},
};
Vue.component("KeepAliveRoute", component);
},
};
項目使用
- 在
main.js中
注冊組件并執(zhí)行緩存邏輯
// main.js
import Vue from "vue";
import App from "./App.vue";
import router from "./router";
// 引入
import { beforeRouterEachHook, KeepAliveRoute } from "./keep-alive-rorute";
// 注冊
Vue.use(KeepAliveRoute);
// 路由守衛(wèi)中執(zhí)行緩存邏輯
router.beforeEach(beforeRouterEachHook());
new Vue({
router,
store,
render: (h) => h(App),
}).$mount("#app");
- 在需要緩存的路由展示區(qū)使用緩存組件
// Home.vue
<template>
<transition name="fade-transform" mode="out-in">
// 使用緩存組件
<KeepAliveRoute>
<router-view></router-view>
</KeepAliveRoute>
</transition>
</template>
- 在路由配置表中配置列表頁
[
{
path: "/list",
name: "List",
meta: {
// 配置需要進入列表頁使用緩存數(shù)據(jù)的頁面
cacheTo: ["Detail"],
},
....
},
{
path: 'detail',
name: 'Detail',
......
},
];
注意
- 路由配置必須使用
name
字段,且和對應的組件name
對應。 - 支持多層級嵌套的父子路由緩存。只需要在父子組件的
router-view
外面包上KeepAliveRoute
組件即可。
查看更多內(nèi)容,請關(guān)注微信公眾號:柒葷捌素