本文介紹幾個(gè)Vue3項(xiàng)目的緩存策略
背景
緩存在性能優(yōu)化中起著重要作用,它可以提高網(wǎng)頁的響應(yīng)速度、給用戶更快速流暢的使用體驗(yàn),同時(shí)還可以減少服務(wù)器負(fù)擔(dān)。接下來從頁面、組件和數(shù)據(jù)三個(gè)層級(jí)說明這次在項(xiàng)目中做的緩存措施。
方案
頁面緩存
頁面緩存適用于內(nèi)容不會(huì)頻繁改變且訪問較多的頁面。通過緩存整個(gè)頁面的HTML內(nèi)容,這樣用戶請(qǐng)求該頁面時(shí),服務(wù)端無需重新生成頁面直接從緩存中獲取即可。由于做了頁面緩存相當(dāng)于組件和數(shù)據(jù)都會(huì)緩存,效率更高一些。然而我們網(wǎng)站所有頁面都會(huì)在右上角展示用戶登錄信息,依賴登錄態(tài)就不適合做頁面緩存了。

組件緩存
接下來考慮做組件緩存。官方給到了一些組件層級(jí)的優(yōu)化點(diǎn),比如:
-
v-once:僅渲染一次,并跳過之后更新,適合本身有一定的渲染成本且無需受父組件的更新影響。API參考 -
v-memo:若條件和上一次相同,則跳過這次更新,適合和v-for配合來優(yōu)化列表,有條件的跳過更新。API參考 -
<keep-alive>:緩存不活動(dòng)的實(shí)例,再次激活時(shí)直接從緩存中恢復(fù)。適合在切換組件中保存組件狀態(tài)。API參考 - 異步組件:僅在實(shí)際渲染時(shí)再加載內(nèi)部邏輯,適合延遲加載非立即顯示的組件。API參考
- 虛擬列表:只渲染當(dāng)前可見項(xiàng)且復(fù)用Dom節(jié)點(diǎn),適合大量數(shù)據(jù)列表或表格列表。推薦的三方庫:vue-virtual-scroller、vue-virtual-scroll-grid、vueuc/VVirtualList。
這里主要說下KeepAlive,它是Vue3官方提供的一個(gè)高級(jí)組件,可以在多個(gè)組件動(dòng)態(tài)切換時(shí)緩存被移除的組件實(shí)例,那具體有哪些應(yīng)用場(chǎng)景呢?先來看下屬性:
-
include:緩存指定name的組件 -
exclude:不緩存指定name的組件 -
max:限制可被緩存的最大組件實(shí)例數(shù),避免占用過大內(nèi)存
舉個(gè)例子,有一個(gè)列表頁(左圖)點(diǎn)擊子項(xiàng)后會(huì)在當(dāng)前頁切換到詳情頁(右圖),如果返回會(huì)重新渲染列表頁,此時(shí)可以通過 <keep-alive>緩存列表頁從而實(shí)現(xiàn)返回后位置不變,更符合用戶習(xí)慣。

項(xiàng)目中用的NuxtPage占位,會(huì)根據(jù)當(dāng)前路由動(dòng)態(tài)地加載和顯示相應(yīng)的頁面組件,也就是圖中GameHome和Details這部分。實(shí)現(xiàn)起來非常簡單(參考方案),只要兩行代碼,首先給想要緩存的列表頁命名如:
defineOptions({ name: "GameHome" });
然后在上一層指定緩存這個(gè)頁面,利用NuxtPage本身keepalive屬性:
<NuxtPage
:keepalive="{ include: ['GameHome'], max: 2 }"
></NuxtPage>
除了可以保存組件狀態(tài),還可以減少不必要的請(qǐng)求和渲染,讓用戶可以更快看到頁面。
數(shù)據(jù)緩存
上面的組件緩存只是客戶端層面的,只能提高單個(gè)用戶在本次瀏覽過程中的體驗(yàn),還可以利用SSR將請(qǐng)求數(shù)據(jù)緩存在服務(wù)端,以提高其他用戶瀏覽同個(gè)高頻頁面的加載速度。思路如下:
- 使用LRUCache緩存算法
private static cache = new LRUCache({
max: 100, // 保存100個(gè)數(shù)據(jù)
ttl: 1000 * 60 * 60 * 24, // 保存1天
});
- 設(shè)置緩存類型,控制緩存范圍,比如個(gè)性化數(shù)據(jù)只緩存在本地
export const enum CacheType {
NONE = 0, // 不緩存
ONLY_CLIENT = 1, // 僅緩存在客戶端
ONLY_SERVER = 2, // 僅緩存在服務(wù)端
BOTH_CLIENT_SERVER = 3, // 客戶端和服務(wù)端都緩存
}
- 在請(qǐng)求接口增加一個(gè)緩存類型參數(shù),滿足條件時(shí)get/set對(duì)應(yīng)緩存
public async request(
name: string,
path: string,
params?: object,
extraOption?: object,
cacheType: CacheType = CacheType.NONE,
) {
return useAsyncData(
name,
() => {
// 1、先獲取緩存數(shù)據(jù),生成唯一緩存key = 請(qǐng)求路徑 + 公參 + 私參
const cacheKey = CacheMgr.generateKey(cacheType, path + this.locale, params);
if (CacheMgr.isCacheScene(cacheType)) {
const cache = CacheMgr.get(cacheKey);
if (cache != null) {
return cache;
}
}
// 2、無可用緩存發(fā)起請(qǐng)求
const response = await $fetch(...);
// 3、統(tǒng)一處理異常情況
this.handleError(response.error);
// 4、添加緩存
if (CacheMgr.isCacheScene(cacheType)) {
CacheMgr.set(response, response.error, cacheKey);
}
return response;
},
extraOption ?? {},
);
}
- 還要注意版本升級(jí)時(shí)清理歷史緩存,避免不可用
總結(jié)
通過以上緩存措施,可以看到,當(dāng)緩存在客戶端時(shí),可以提高用戶多次訪問同一頁面/模塊的速度;當(dāng)緩存在服務(wù)端時(shí),還能提高其他用戶訪問同一頁面/模塊的速度,同時(shí)給服務(wù)器減壓。