這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 為了讓頁面保活更加穩定,你們是怎麼做的? 我用一行配置實現了 Vue頁面保活是指在用戶離開當前頁面後,可以在返回時恢覆上一次瀏覽頁面的狀態。這種技術可以讓用戶享受更加流暢自然的瀏覽體驗,而不會被繁瑣的操作打擾。 為什麼需要頁面保活? 頁面 ...
這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助
為了讓頁面保活更加穩定,你們是怎麼做的?
我用一行配置實現了
Vue頁面保活是指在用戶離開當前頁面後,可以在返回時恢覆上一次瀏覽頁面的狀態。這種技術可以讓用戶享受更加流暢自然的瀏覽體驗,而不會被繁瑣的操作打擾。
為什麼需要頁面保活?
頁面保活可以提高用戶的體驗感。例如,當用戶從一個帶有分頁的表格頁面(【頁面A】)跳轉到數據詳情頁面(【頁面B】),並查看了數據之後,當用戶從【頁面B】返回【頁面A】時,如果沒有頁面保活,【頁面A】會重新載入並跳轉到第一頁,這會讓用戶感到非常煩惱,因為他們需要重新選擇頁面和數據。因此,使用頁面保活技術,當用戶返回【頁面A】時,可以恢復之前選擇的頁碼和數據,讓用戶的體驗更加流暢。
如何實現頁面保活?
狀態存儲
這個方案最為直觀,原理就是在離開【頁面A】之前手動將需要保活的狀態存儲起來。可以將狀態存儲到LocalStore
、SessionStore
或IndexedDB
。在【頁面A】組件的onMounted
鉤子中,檢測是否存在此前的狀態,如果存在從外部存儲中將狀態恢復回來。
有什麼問題?
- 浪費心智(麻煩/操心)。這個方案存在的問題就是,需要在編寫組件的時候就明確的知道跳轉到某些頁面時進行狀態存儲。
- 無法解決子組件狀態。在頁面組件中還可以做到保存頁面組件的狀態,但是如何保存子組件呢。不可能所有的子組件狀態都在頁面組件中維護,因為這樣的結構並不是合理。
組件緩存
利用Vue
的內置組件<KeepAlive/>
緩存包裹在其中的動態切換組件(也就是<Component/>
組件)。<KeepAlive/>
包裹動態組件時,會緩存不活躍的組件,而不是銷毀它們。當一個組件在<KeepAlive/>
中被切換時,activated
和deactivated
生命周期鉤子會替換mounted
和unmounted
鉤子。最關鍵的是,<KeepAlive/>
不僅適用於被包裹組件的根節點,也適用於其子孫節點。
<KeepAlive/>
搭配vue-router
即可實現頁面的保活,實現代碼如下:
<template> <RouterView v-slot="{ Component }"> <KeepAlive> <component :is="Component"/> </KeepAlive> </RouterView> </template>
有什麼問題?
- 頁面保活不准確。上面的方式雖然實現了頁面保活,但是並不能滿足生產要求,例如:【頁面A】是應用首頁,【頁面B】是數據列表頁,【頁面C】是數據詳情頁。用戶查看數據詳情的動線是:【頁面A】->【頁面B】->【頁面C】,在這條動線中【頁面B】->【頁面C】的時候需要緩存【頁面B】,當從【頁面C】->【頁面B】的時候需要從換從中恢復【頁面B】。但是【頁面B】->【頁面A】的時候又不需要緩存【頁面B】,上面的這個方法並不能做到這樣的配置。
最佳實踐
最理想的保活方式是,不入侵組件代碼的情況下,通過簡單的配置實現按需的頁面保活。
【不入侵組件代碼】這條即可排除第一種方式的實現,第二種【組件緩存】的方式只是敗在了【按需的頁面保活】。那麼改造第二種方式,通過在router
的路由配置上進行按需保活的配置,再提供一種讀取配置結合<KeepAlive/>
的include
屬性即可。
路由配置
src/router/index.ts
import useRoutersStore from '@/store/routers'; const routes: RouteRecordRaw[] = [ { path: '/', name: 'index', component: () => import('@/layout/index.vue'), children: [ { path: '/app', name: 'App', component: () => import('@/views/app/index.vue'), }, { path: '/data-list', name: 'DataList', component: () => import('@/views/data-list/index.vue'), meta: { // 離開【/data-list】前往【/data-detail】時緩存【/data-list】 leaveCaches: ['/data-detail'], } }, { path: '/data-detail', name: 'DataDetail', component: () => import('@/views/data-detail/index.vue'), } ] } ]; router.beforeEach((to: RouteLocationNormalized, from: RouteLocationNormalized, next: NavigationGuardNext) => { const { cacheRouter } = useRoutersStore(); cacheRouter(from, to); next(); });
保活組件存儲
src/stroe/router.ts
import { RouteLocationNormalized } from 'vue-router'; const useRouterStore = defineStore('router', { state: () => ({ cacheComps: new Set<string>(), }), actions: { cacheRouter(from: RouteLocationNormalized, to: RouteLocationNormalized) { if( Array.isArray(from.meta.leaveCaches) && from.meta.leaveCaches.inclued(to.path) && typeof from.name === 'string' ) { this.cacheComps.add(form.name); } if( Array.isArray(to.meta.leaveCaches) && !to.meta.leaveCaches.inclued(from.path) && typeof to.name === 'string' ) { this.cacheComps.delete(to.name); } }, }, getters: { keepAliveComps(state: State) { return [...state.cacheComps]; }, }, });
頁面緩存
src/layout/index.vue
<template> <RouterView v-slot="{ Component }"> <KeepAlive :include="keepAliveComps"> <component :is="Component"/> </KeepAlive> </RouterView> </template> <script lang='ts' setup> import { storeToRefs } from 'pinia'; import useRouterStore from '@/store/router'; const { keepAliveComps } = storeToRefs(useRouterStore()); </script>
TypeScript提升配置體驗
import 'vue-router'; export type LeaveCaches = string[]; declare module 'vue-router' { interface RouteMeta { leaveCaches?: LeaveCaches; } }
該方案的問題
- 缺少通配符處理
/*
、/**/index
。 - 無法緩存
/preview/:address
這樣的動態路由。 - 組件名和路由名稱必須保持一致。
總結
通過<RouterView v-slot="{ Component }">
獲取到當前路由對應的組件,在將該組件通過<component :is="Component" />
渲染,渲染之前利用<KeepAlive :include="keepAliveComps">
來過濾當前組件是否需要保活。 基於上述機制,通過簡單的路由配置中的meta.leaveCaches = [...]
來配置從當前路由出發到哪些路由時,需要緩存當前路由的內容。