一勞永逸搞定列表頁(yè)面緩存

背景介紹

在前端日常項(xiàng)目開發(fā)中,大家應(yīng)該都有遇到過這樣的需求:當(dāng)我們?cè)诹斜眄?yè)輸入條件進(jìn)行數(shù)據(jù)篩選時(shí),我們可能需要根據(jù)篩選后的結(jié)果進(jìn)入到某一條的詳情頁(yè),當(dāng)我們看完詳情頁(yè)內(nèi)容,再返回列表頁(yè),此時(shí)我們希望還是之前的篩選結(jié)果,包括條件和分頁(yè)等。

在實(shí)際開發(fā)中,改變頁(yè)面導(dǎo)航的路由都會(huì)重新匹配頁(yè)面,初始化頁(yè)面的數(shù)據(jù)狀態(tài),無法直接達(dá)到這種效果。但Vue給我們提供了“秘密武器”-keep-alive,可以實(shí)現(xiàn)頁(yè)面的數(shù)據(jù)緩存。

其實(shí)以前也寫過類似的實(shí)現(xiàn)頁(yè)面緩存的方案:Vue-Router 實(shí)現(xiàn)前端頁(yè)面緩存,是通過keep-aliveinclude屬性控制要緩存的頁(yè)面。但是這種方式不夠靈活,一旦緩存了頁(yè)面,從其他頁(yè)面(非詳情頁(yè)面)跳入到列表頁(yè)時(shí)數(shù)據(jù)也不會(huì)重新更新,需要在組件路由守衛(wèi)beforeRouteEnter中去做判斷處理,十分麻煩。

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

實(shí)現(xiàn)思路

總體思路:

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

思路分析

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

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

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

具體實(shí)現(xiàn)

前置須知

  • 在路由表配置中維護(hù)一個(gè)cacheTo字段,如果配置了該字段就表示該路由為列表頁(yè)。
  • 控制緩存的邏輯,我們單獨(dú)在一個(gè)文件內(nèi)完成,作為組件單獨(dú)管理。為了避免include的管理更加簡(jiǎn)單,就不適用vuex,這里使用Vue.observable。
  • 為了避免維護(hù)keep-aliveinclude屬性對(duì)代碼造成侵入,這里使用函數(shù)式組件。
  • 為了在管理緩存的組件內(nèi)能獲取到tofrom,需要在組件內(nèi)注冊(cè)一個(gè)beforeEach鉤子,vue-routerbeforeEach鉤子是可以重復(fù)注冊(cè)的,按照注冊(cè)順序執(zhí)行。

實(shí)現(xiàn)細(xì)節(jié)

// keep-alive-rorute.js
import Vue from "vue";
/**
 * 創(chuàng)建存儲(chǔ)緩存?zhèn)}庫(kù)
 */
// 存儲(chǔ)需要緩存頁(yè)面路由
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();
// 路由進(jìn)入鉤子函數(shù)
export const beforeRouterEachHook = (hook = defaultHook) => {
  return (to, from, next) => {
    /**
     * 思路:只要是類列表頁(yè),進(jìn)入之前都將其緩存,離開時(shí)判斷新路由是否是該類列表頁(yè)指定的類詳情頁(yè),如果不是就清除緩存。
     */
    // to頁(yè)面路由
    const toRouteName = to.name;
    // to頁(yè)面為列表頁(yè)時(shí)配置的to路由集合
    const toCacheRoutes = (to.meta || {}).cacheTo;
    // to頁(yè)面是否是列表頁(yè)
    const isToPageList = toCacheRoutes && toCacheRoutes.length;
    // from頁(yè)面路由名稱
    const fromRouteName = from.name;
    // form頁(yè)面為列表頁(yè)時(shí),配置的to頁(yè)面路由集合
    const fromCacheRoutes = (from.meta || {}).cacheTo;
    // from頁(yè)面是否是列表頁(yè)
    const isFromPageList = fromCacheRoutes && fromCacheRoutes.length;

    // 1如果to,from都不是列表頁(yè),清除所有緩存
    if (!isToPageList && !isFromPageList) {
      clearCache();
    } else if (isToPageList && isFromPageList) {
      // 2如果to,from都是列表頁(yè)
      // 如果to列表頁(yè)在from列表頁(yè)的配置中就要緩存,否則不用
      if (fromCacheRoutes.indexOf(toRouteName) < 0) {
        clearCache();
      }
      if (to.matched && to.matched.length > 2) {
        // 三級(jí)路由時(shí),緩存父子兩級(jí)路由
        to.matched.map((element) => {
          if (element.name) {
            addCache(element.name);
          }
        });
      } else {
        // 二級(jí)路由緩存當(dāng)前路由
        addCache(toRouteName);
      }
    } else if (isToPageList) {
      // 3如果to是列表頁(yè)
      // 如果from不在to的配置中,清除所有緩存,同時(shí)緩存新路由
      if (toCacheRoutes.indexOf(fromRouteName) < 0) {
        clearCache();
        if (to.matched && to.matched.length > 2) {
          // 三級(jí)路由時(shí)緩存父子兩級(jí)路由
          to.matched.map((element) => {
            if (element.name) {
              addCache(element.name);
            }
          });
        } else {
          // 二級(jí)路由緩存當(dāng)前路由
          addCache(toRouteName);
        }
      }
    } else if (isFromPageList) {
      // 4如果from頁(yè)面是列表頁(yè)
      // 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);
  },
};

項(xiàng)目使用

  • main.js中注冊(cè)組件并執(zhí)行緩存邏輯
// main.js
import Vue from "vue";
import App from "./App.vue";
import router from "./router";
// 引入
import { beforeRouterEachHook, KeepAliveRoute } from "./keep-alive-rorute";
// 注冊(cè)
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>
  • 在路由配置表中配置列表頁(yè)
[
  {
    path: "/list",
    name: "List",
    meta: {
     // 配置需要進(jìn)入列表頁(yè)使用緩存數(shù)據(jù)的頁(yè)面
      cacheTo: ["Detail"],
    },
    ....
  },
  {
      path: 'detail',
      name: 'Detail',
      ......
  },
];

注意

  • 路由配置必須使用name字段,且和對(duì)應(yīng)的組件name對(duì)應(yīng)。
  • 支持多層級(jí)嵌套的父子路由緩存。只需要在父子組件的router-view外面包上KeepAliveRoute組件即可。

查看更多內(nèi)容,請(qǐng)關(guān)注微信公眾號(hào):柒葷捌素

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 今天感恩節(jié)哎,感謝一直在我身邊的親朋好友。感恩相遇!感恩不離不棄。 中午開了第一次的黨會(huì),身份的轉(zhuǎn)變要...
    余生動(dòng)聽閱讀 10,798評(píng)論 0 11
  • 彩排完,天已黑
    劉凱書法閱讀 4,452評(píng)論 1 3
  • 表情是什么,我認(rèn)為表情就是表現(xiàn)出來的情緒。表情可以傳達(dá)很多信息。高興了當(dāng)然就笑了,難過就哭了。兩者是相互影響密不可...
    Persistenc_6aea閱讀 129,398評(píng)論 2 7

友情鏈接更多精彩內(nèi)容