Vue3 中 keepAlive 如何搭配 VueRouter 來更自由的控制頁面的狀態緩存?

来源:https://www.cnblogs.com/zdsdididi/archive/2023/08/24/17653695.html
-Advertisement-
Play Games

在 vue 中,預設情況下,一個組件實例在被替換掉後會被銷毀。這會導致它丟失其中所有已變化的狀態——當這個組件再一次被顯示時,會創建一個只帶有初始狀態的新實例。但是 vue 提供了 keep-alive 組件,它可以將一個動態組件包裝起來從而實現組件切換時候保留其狀態。本篇文章要介紹的並不是它的基本 ...


在 vue 中,預設情況下,一個組件實例在被替換掉後會被銷毀。這會導致它丟失其中所有已變化的狀態——當這個組件再一次被顯示時,會創建一個只帶有初始狀態的新實例。但是 vue 提供了 keep-alive 組件,它可以將一個動態組件包裝起來從而實現組件切換時候保留其狀態。本篇文章要介紹的並不是它的基本使用方法(這些官網文檔已經寫的很清楚了),而是它如何結合 VueRouter 來更自由的控制頁面狀態的緩存

全部緩存

我們先搭建一個 Vue 項目,裡面有三個頁面a,b,c,並給它們一些相互跳轉的邏輯和狀態

  • a 頁面
<template>
  <div>
    <div>A頁面</div>
    <input type="text" v-model="dataA" /><br />
    <div @click="toB">跳轉B</div>
    <div @click="toC">跳轉C</div>
  </div>
</template>

<script lang="ts" setup>
import { ref } from "vue";
import { useRouter, useRoute } from "vue-router";
const router = useRouter();
const route = useRoute();
const dataA = ref("");
const toB = () => {
  router.push("/bb");
};
const toC = () => {
  router.push("/cc");
};
</script>
  • b 頁面
<template>
  <div>
    <div>B頁面</div>
    <input type="text" v-model="dataB" /><br />
    <div @click="toA">跳轉A</div>
  </div>
</template>

<script lang="ts" setup>
import { ref } from "vue";
import { useRouter } from "vue-router";
const router = useRouter();
const dataB = ref("");
const toA = () => {
  router.push("/aa");
};
</script>
  • c 頁面
<template>
  <div>
    <div>C頁面</div>
    <input type="text" v-model="dataC" />
    <div @click="toA">跳轉A</div>
  </div>
</template>

<script lang="ts" setup name="C">
import { ref } from "vue";
import { useRouter } from "vue-router";
const router = useRouter();
const dataC = ref("");
const toA = () => {
  router.push("/aa");
};
</script>

然後在 route/index.ts 寫下它們對應的路由配置

import { createRouter, createWebHashHistory, RouteRecordRaw } from "vue-router";

const routes: RouteRecordRaw[] = [
  {
    path: "/aa",
    name: "a",
    component: () => import(/* webpackChunkName: "A" */ "../views/a.vue"),
  },
  {
    path: "/bb",
    name: "b",
    component: () => import(/* webpackChunkName: "B" */ "../views/b.vue"),
  },
  {
    path: "/cc",
    name: "c",
    component: () => import(/* webpackChunkName: "C" */ "../views/c.vue"),
  },
];

const router = createRouter({
  history: createWebHashHistory(),
  routes,
});

export default router;

在 App.vue 中我們用 keep-alive 將 router-view 進行包裹

<template>
  <keep-alive>
    <router-view />
  </keep-alive>
</template>

啟動項目,測試一下頁面狀態有沒有被緩存

GIF11.gif

此時我們發現狀態並沒有緩存,並且控制台還給了個警告

image.png

上面的寫法在 vue2 中是可以的,但是在 vue3 中需要將 keep-alive 寫在 router-view 中才行,我們修改一下寫法

<template>
  <router-view v-slot="{ Component }">
    <keep-alive>
      <component :is="Component" />
    </keep-alive>
  </router-view>
</template>

這種寫法其實就是 router-view 組件的插槽傳遞了一個帶有當前組件的組件名 Component 的對象,然後用 keep-alive 包裹一個動態組件(回歸原始寫法)。

我們再試一下頁面的緩存效果,這時候發現頁面的狀態被緩存了

GIF22.gif

緩存指定頁面

通常情況下我們並不想將所有頁面狀態都緩存,而只想緩存部分頁面,這樣的話該怎麼做呢?

其實我們可以在 template 中通過$route 獲取路由的信息,所以我們可以在需要緩存的頁面配置一下 meta 對象,比如 a 頁面我們想緩存其狀態,可以將 keepAlive 設置位 true

//route/index.ts

const routes: RouteRecordRaw[] = [
  {
    path: "/aa",
    name: "a",
    meta: {
      keepAlive: true,
    },
    component: () => import(/* webpackChunkName: "A" */ "../views/a.vue"),
  },
  ...
];

然後回到 App.vue 中判斷 keepAlive 來決定是否緩存

<template>
  <router-view v-slot="{ Component }">
    <keep-alive>
      <component v-if="$route.meta.keepAlive" :is="Component" />
    </keep-alive>
    <component v-if="!$route.meta.keepAlive" :is="Component" />
  </router-view>
</template>

再看下效果

GIF33.gif

此時我們發現 a 頁面狀態被緩存,b 頁面的狀態沒有緩存

但是有時候我們想要這樣一個效果

a 跳轉 b 的時候我們需要緩存 a 頁面狀態,但是當 a 跳轉 c 的時候我們不需要緩存 a 頁面,此時我們該如何做呢?

或許有的同學想到了這樣一個方法,當 a 跳轉 c 的時候將 a 頁面的緩存刪除,這樣就實現了上面的效果。可惜我找了半天也沒找到 vue3 中刪除指定頁面緩存的方法

我也嘗試過跳轉 c 頁面的時候將 a 的 keepAlive 設置為 false,但是再次回到 a 頁面的時候 keepAlive 會重置,a 頁面狀態依然會被緩存。

既然如此為了做到更精細的緩存控制只有使用 keep-alive 中的 inclue 屬性了

使用 inclue 控制頁面緩存

keep-alive 預設會緩存內部的所有組件實例,但我們可以通過 include 來定製該行為。它的值都可以是一個以英文逗號分隔的字元串、一個正則表達式,或是一個數組。這裡我們使用一個數組來維護需要緩存的組件頁面,註意這個數組中是組件的名字而不是路由的 name

在 vue3 中給組件命名可以這樣寫

<script lang='ts'>
export default {
    name: 'MyComponent',
}
</script>

但是我們通常會使用 setup 語法,這樣的話我們得寫兩個script標簽,太麻煩。我們可以使用插件vite-plugin-vue-setup-extend處理

npm i vite-plugin-vue-setup-extend -D

然後在vite.config.ts中引入這個插件就可以使用了

import { defineConfig, Plugin } from "vite";
import vue from "@vitejs/plugin-vue";
import vueSetupExtend from "vite-plugin-vue-setup-extend";

export default defineConfig({
  plugins: [vue(), vueSetupExtend()],
});

然後就可以這樣命名了

<script lang="ts" setup name="A"></script>

下麵我們修改一下 App.vue

<template>
  <router-view v-slot="{ Component }">
    <keep-alive :include="['A']">
      <component :is="Component" />
    </keep-alive>
  </router-view>
</template>

這其實就代表組件名為 A 的 頁面才會被緩存,接下來我們要做的就是控制這個數組來決定頁面的緩存,但是這個數組要放在哪裡維護呢? 答案肯定是放到全局狀態管理器中拉。所以我們引入 Pinia 作為全局狀態管理器

npm i pinia

在 main.ts 中註冊

import { createPinia } from "pinia";
const Pinia = createPinia();
createApp(App).use(route).use(Pinia).use(RouterViewKeepAlive).mount("#app");

新建 store/index.ts

import { defineStore } from "pinia";

export default defineStore("index", {
  state: (): { cacheRouteList: string[] } => {
    return {
      cacheRouteList: [],
    };
  },
  actions: {
    //添加緩存組件
    addCacheRoute(name: string) {
      this.cacheRouteList.push(name);
    },
    //刪除緩存組件
    removeCacheRoute(name: string) {
      for (let i = this.cacheRouteList.length - 1; i >= 0; i--) {
        if (this.cacheRouteList[i] === name) {
          this.cacheRouteList.splice(i, 1);
        }
      }
    },
  },
});

在 App.vue 中使用 cacheRouteList

<template>
  <router-view v-slot="{ Component }">
    <keep-alive :include="catchStore.cacheRouteList">
      <component :is="Component" />
    </keep-alive>
  </router-view>
</template>
<script lang="ts" setup>
import cache from "./store";
const catchStore = cache();
</script>

此時就可以根據 cacheRouteList 控制緩存頁面了。

此時我們再來實現前面提到的問題a 跳轉 b 的時候我們需要緩存 a 頁面狀態,但是當 a 跳轉 c 的時候我們不需要緩存 a 頁面就很簡單了

import cache from "../store";
const catchStore = cache();
const router = useRouter();

const toB = () => {
  catchStore.addCacheRoute("A");
  router.push("/bb");
};
const toC = () => {
  catchStore.removeCacheRoute("A");
  router.push("/cc");
};

此時再看下頁面的效果

GIF44.gif

可以發現 a 到 c 後再回來狀態就重置了,這樣不僅做到了上述效果,還可以讓你隨時隨地的去刪除指定組件的緩存。

到這裡我們便完成了使用 inclue 對頁面狀態緩存進行更精細化的控制。當然,如果你有更好的方案歡迎在評論區指出,一起討論探索


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • # 記錄http請求 ## 環境 * .net7 ## 一、過濾器(Filter) 這個過程用的的是操作過濾器(`ActionFilter`) ## 二、 ### 2.1 繼承`IAsyncActionFilter` ### 2.2 重寫`OnActionExecutionAsync` `OnAct ...
  • [toc] # Linux運維工程師面試題(2) > 祝各位小伙伴們早日找到自己心儀的工作。 > 持續學習才不會被淘汰。 > 地球不爆炸,我們不放假。 > 機會總是留給有有準備的人的。 > 加油,打工人! ## 1 訪問一個網站的流程 1. 打開瀏覽器,輸入網址。首先查找本地緩存,如果有就打開頁面, ...
  • 操作系統是電腦不可或缺的一部分,它連接著硬體和應用程式。內核是操作系統的核心,負責管理進程和線程、記憶體、硬體設備以及提供系統調用介面。電腦啟動過程中,ROM負責載入並執行BIOS程式,而RAM用於存儲運行中的程式和數據。系統調用是操作系統提供給應用程式的介面,通過系統調用可以訪問操作系統的功能。... ...
  • ![](https://img2023.cnblogs.com/blog/3076680/202308/3076680-20230822120346228-1599813347.png) # 1. 不需要考慮排除任何列 ## 1.1. 清除數據表中所有的內容 ## 1.2. 暫存新數據倉庫的數據 # ...
  • [數據治理](https://www.dtstack.com/?src=szsm)是推動大型集團企業轉型升級、提升競爭優勢、實現高質量發展的重要引擎。 通過搭建[大數據平臺](https://www.dtstack.com/?src=szsm),實現對業務系統數據的採集、清理、建模、整合,建立一個符 ...
  • HP DP(Data Protector Manager)上一個剛剛遷移升級的資料庫備份作業失敗,具體失敗信息如下 .................................RMAN-08503: piece handle=c-1684727642-20230822-00 comment=A ...
  • # [TOC] [Android開發中的NDK到底是什麼?(詳細解析+案例) - 知乎 (zhihu.com)](https://zhuanlan.zhihu.com/p/415536928) # NDK介紹 **(1)簡介** **定義:**`Native Development Kit`,是 ` ...
  • 我司存在記憶體為1G RAM的設備,屬於低記憶體設備,經常會出現記憶體很緊張的場景,也容易因此導致一系列七七八八的邊際問題,故有必要瞭解Android系統的記憶體相關知識: 1. 瞭解記憶體的分配、回收方式 2. 瞭解OOM、LMK的相關機制 3. 瞭解Android系統記憶體相關調試方式 4. 瞭解Andro... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...