一勞永逸搞定列表頁面緩存

背景介紹

在前端日常項目開發(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-aliveinclude屬性控制要緩存的頁面。但是這種方式不夠靈活,一旦緩存了頁面,從其他頁面(非詳情頁面)跳入到列表頁時數(shù)據(jù)也不會重新更新,需要在組件路由守衛(wèi)beforeRouteEnter中去做判斷處理,十分麻煩。

因此,經(jīng)過參考探索,今天來分享一個一勞永逸的實現(xiàn)頁面緩存的方案。

實現(xiàn)思路

總體思路:

只要是列表頁(需要使用緩存的頁面),進入之前,都將其添加到要緩存的頁面中,離開列表頁時判斷新(to)路由是否是該列表頁指定的詳情頁(配置路由時通過 cacheTo 字段來指定),如果不是就清空緩存。

思路分析

根據(jù)總體思路,主要分為以下四種情況:

  1. fromto都不是列表頁
    • 都不需要緩存,清空以前的緩存
  2. fromto都是列表頁
    • 2.1-如果to不在from的配置中,清空緩存,新增to緩存
    • 2.2-如果tofrom的配置中,保留from緩存,新增to緩存
  3. to是列表頁,from不是
    • 3.1-from不在to的配置中,清空緩存,新增to緩存
    • 3.2-fromto的配置中,不做處理(因為 3.1 已經(jīng)緩存了 to)
  4. from是列表頁,to不是
    • 4.1-to不在from的配置中,清空緩存
    • 4.2-tofrom的配置中,不做處理(因為 from 已經(jīng)在 3.1 時緩存)

以上思路就是日常所有的場景,理解起來可能會比較繞,結(jié)合實際場景理解就會發(fā)現(xiàn)所有情況都已經(jīng)覆蓋到。

具體實現(xiàn)

前置須知

  • 在路由表配置中維護一個cacheTo字段,如果配置了該字段就表示該路由為列表頁。
  • 控制緩存的邏輯,我們單獨在一個文件內(nèi)完成,作為組件單獨管理。為了避免include的管理更加簡單,就不適用vuex,這里使用Vue.observable
  • 為了避免維護keep-aliveinclude屬性對代碼造成侵入,這里使用函數(shù)式組件。
  • 為了在管理緩存的組件內(nèi)能獲取到tofrom,需要在組件內(nèi)注冊一個beforeEach鉤子,vue-routerbeforeEach鉤子是可以重復注冊的,按照注冊順序執(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)注微信公眾號:柒葷捌素

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 今天感恩節(jié)哎,感謝一直在我身邊的親朋好友。感恩相遇!感恩不離不棄。 中午開了第一次的黨會,身份的轉(zhuǎn)變要...
    迷月閃星情閱讀 10,606評論 0 11
  • 彩排完,天已黑
    劉凱書法閱讀 4,273評論 1 3
  • 表情是什么,我認為表情就是表現(xiàn)出來的情緒。表情可以傳達很多信息。高興了當然就笑了,難過就哭了。兩者是相互影響密不可...
    Persistenc_6aea閱讀 125,919評論 2 7