在開發(fā) web 應(yīng)用程序時(shí),性能是一個(gè)重要的話題。為了提高用戶體驗(yàn)和節(jié)省網(wǎng)絡(luò)資源,我們需要對(duì)一些頻繁或重復(fù)的 API 請(qǐng)求進(jìn)行緩存,以減少不必要的服務(wù)器交互。緩存的原理是將第一次請(qǐng)求的數(shù)據(jù)保存在客戶端,之后再次請(qǐng)求時(shí)直接從緩存中獲取,而不是向服務(wù)器發(fā)送請(qǐng)求。
緩存的方式有很多種,根據(jù)不同的業(yè)務(wù)場(chǎng)景和需求,我們可以選擇合適的方案來(lái)實(shí)現(xiàn)緩存功能。本文將介紹前端 API 請(qǐng)求緩存的三種方案,從簡(jiǎn)單到復(fù)雜,分別是:
一、數(shù)據(jù)緩存
- 數(shù)據(jù)緩存是最簡(jiǎn)單的一種緩存方案,它的思路是將第一次請(qǐng)求的數(shù)據(jù)保存在一個(gè) Map 對(duì)象中,以 API 的名稱或參數(shù)作為鍵,以數(shù)據(jù)作為值。之后再次請(qǐng)求時(shí),先從 Map 對(duì)象中查找是否有對(duì)應(yīng)的數(shù)據(jù),如果有則直接返回,如果沒(méi)有則向服務(wù)器發(fā)送請(qǐng)求,并將返回的數(shù)據(jù)保存在 Map 對(duì)象中。
- 數(shù)據(jù)緩存的優(yōu)點(diǎn)是實(shí)現(xiàn)簡(jiǎn)單,適用于一些不需要更新的靜態(tài)數(shù)據(jù)。缺點(diǎn)是無(wú)法處理同時(shí)多次請(qǐng)求的情況,也無(wú)法設(shè)置緩存的有效期,可能會(huì)導(dǎo)致數(shù)據(jù)過(guò)時(shí)或冗余。
示例代碼如下:
// 創(chuàng)建一個(gè) Map 對(duì)象用于存儲(chǔ)數(shù)據(jù)
const dataCache = new Map();
// 定義一個(gè)獲取商品列表的 API
async function getWares() {
// 以 API 的名稱作為鍵
let key = "wares";
// 從 Map 對(duì)象中獲取數(shù)據(jù)
let data = dataCache.getkey;
if (!data) {
// 如果沒(méi)有數(shù)據(jù),向服務(wù)器發(fā)送請(qǐng)求
const res = await request.get("/getWares");
// 對(duì)返回的數(shù)據(jù)進(jìn)行一些處理
data = res.data;
// 將數(shù)據(jù)保存在 Map 對(duì)象中
dataCache.set(key, data);
}
// 返回?cái)?shù)據(jù)
return data;
}
調(diào)用方式如下:
// 第一次調(diào)用,向服務(wù)器發(fā)送請(qǐng)求,獲取數(shù)據(jù)
getWares().then((data) => {
console.log(data);
});
// 第二次調(diào)用,從 Map 對(duì)象中獲取數(shù)據(jù),不再發(fā)送請(qǐng)求
getWares().then((data) => {
console.log(data);
});
二、Promise 緩存
- Promise 緩存是對(duì)數(shù)據(jù)緩存的改進(jìn),它的思路是將第一次請(qǐng)求的 Promise 對(duì)象保存在一個(gè) Map 對(duì)象中,以 API 的名稱或參數(shù)作為鍵,以 Promise 對(duì)象作為值。之后再次請(qǐng)求時(shí),先從 Map 對(duì)象中查找是否有對(duì)應(yīng)的 Promise 對(duì)象,如果有則直接返回,如果沒(méi)有則向服務(wù)器發(fā)送請(qǐng)求,并將返回的 Promise 對(duì)象保存在 Map 對(duì)象中。
- Promise 緩存的優(yōu)點(diǎn)是可以處理同時(shí)多次請(qǐng)求的情況,避免重復(fù)發(fā)送請(qǐng)求,只有第一次請(qǐng)求會(huì)向服務(wù)器發(fā)送請(qǐng)求,之后的請(qǐng)求都會(huì)等待第一次請(qǐng)求的結(jié)果。缺點(diǎn)是無(wú)法設(shè)置緩存的有效期,可能會(huì)導(dǎo)致數(shù)據(jù)過(guò)時(shí)或冗余。
示例代碼如下:
// 創(chuàng)建一個(gè) Map 對(duì)象用于存儲(chǔ) Promise 對(duì)象
const promiseCache = new Map();
// 定義一個(gè)獲取商品列表的 API
function getWares() {
// 以 API 的名稱作為鍵
let key = "wares";
// 從 Map 對(duì)象中獲取 Promise 對(duì)象
let promise = promiseCache.getkey;
// 如果沒(méi)有 Promise 對(duì)象
if (!promise) {
// 向服務(wù)器發(fā)送請(qǐng)求,并返回一個(gè) Promise 對(duì)象
promise = request.get("/getWares").then((res) => {
// 對(duì)返回的數(shù)據(jù)進(jìn)行一些處理
return res.data;
}).catch((error) => {
// 如果請(qǐng)求出錯(cuò),從 Map 對(duì)象中刪除 Promise 對(duì)象,避免緩存錯(cuò)誤的結(jié)果
promiseCache.deletekey;
// 拋出錯(cuò)誤
return Promise.reject(error);
});
// 將 Promise 對(duì)象保存在 Map 對(duì)象中
promiseCache.set(key, promise);
}
// 返回 Promise 對(duì)象
return promise;
}
調(diào)用方式如下:
// 第一次調(diào)用,向服務(wù)器發(fā)送請(qǐng)求,獲取數(shù)據(jù)
getWares().then((data) => {
console.log(data);
});
// 第二次調(diào)用,從 Map 對(duì)象中獲取 Promise 對(duì)象,不再發(fā)送請(qǐng)求,等待第一次請(qǐng)求的結(jié)果
getWares().then((data) => {
console.log(data);
});
三、多 Promise 緩存
- 多 Promise 緩存是對(duì) Promise 緩存的擴(kuò)展,它的思路是將多個(gè) API 請(qǐng)求的 Promise 對(duì)象保存在一個(gè) Map 對(duì)象中,以 API 的名稱或參數(shù)作為鍵,以 Promise 對(duì)象作為值。之后再次請(qǐng)求時(shí),先從 Map 對(duì)象中查找是否有對(duì)應(yīng)的 Promise 對(duì)象,如果有則直接返回,如果沒(méi)有則向服務(wù)器發(fā)送請(qǐng)求,并將返回的 Promise 對(duì)象保存在 Map 對(duì)象中。最后,使用 Promise.all 方法將多個(gè) Promise 對(duì)象合并為一個(gè) Promise 對(duì)象,統(tǒng)一返回結(jié)果。
- 多 Promise 緩存的優(yōu)點(diǎn)是可以同時(shí)獲取多個(gè) API 請(qǐng)求的數(shù)據(jù),提高效率,只有第一次請(qǐng)求會(huì)向服務(wù)器發(fā)送請(qǐng)求,之后的請(qǐng)求都會(huì)等待第一次請(qǐng)求的結(jié)果。缺點(diǎn)是無(wú)法設(shè)置緩存的有效期,可能會(huì)導(dǎo)致數(shù)據(jù)過(guò)時(shí)或冗余。另外,如果其中一個(gè) API 請(qǐng)求出錯(cuò),會(huì)導(dǎo)致整個(gè) Promise 對(duì)象被拒絕,無(wú)法獲取其他 API 請(qǐng)求的數(shù)據(jù)。
示例代碼如下:
// 定義一個(gè)對(duì)象,存儲(chǔ) API 的名稱和對(duì)應(yīng)的請(qǐng)求地址
const querys = {
wares: "/getWares",
skus: "/getSku",
};
// 創(chuàng)建一個(gè) Map 對(duì)象用于存儲(chǔ) Promise 對(duì)象
const promiseCache = new Map();
// 定義一個(gè)函數(shù),用于同時(shí)獲取多個(gè) API 請(qǐng)求的數(shù)據(jù)
async function queryAll(queryApiName) {
// 判斷傳入的參數(shù)是否是數(shù)組
const queryIsArray = Array.isArray(queryApiName);
// 統(tǒng)一處理參數(shù),無(wú)論是字符串還是數(shù)組,都視為數(shù)組
const apis = queryIsArray ? queryApiName : [queryApiName];
// 創(chuàng)建一個(gè)數(shù)組,用于存儲(chǔ)所有的 Promise 對(duì)象
const promiseApi = [];
// 遍歷參數(shù)數(shù)組,對(duì)每個(gè) API 進(jìn)行處理
apis.forEach((api) => {
// 從 Map 對(duì)象中獲取 Promise 對(duì)象
let promise = promiseCache.get(api);
// 如果沒(méi)有 Promise 對(duì)象
if (!promise) {
// 向服務(wù)器發(fā)送請(qǐng)求,并返回一個(gè) Promise 對(duì)象
promise = request.get(querys[api]).then((res) => {
// 對(duì)返回的數(shù)據(jù)進(jìn)行一些處理
return res.data;
}).catch((error) => {
// 如果請(qǐng)求出錯(cuò),從 Map 對(duì)象中刪除 Promise 對(duì)象,避免緩存錯(cuò)誤的結(jié)果
promiseCache.delete(api);
// 拋出錯(cuò)誤
return Promise.reject(error);
});
// 將 Promise 對(duì)象保存在 Map 對(duì)象中
promiseCache.set(api, promise);
}
// 將 Promise 對(duì)象添加到數(shù)組中
promiseApi.push(promise);
});
// 使用 Promise.all 方法將多個(gè) Promise 對(duì)象合并為一個(gè) Promise 對(duì)象
return Promise.all(promiseApi).then((res) => {
// 根據(jù)傳入的參數(shù)是字符串還是數(shù)組來(lái)返回?cái)?shù)據(jù),因?yàn)楸旧矶际菙?shù)組操作
// 如果傳入的是字符串,則需要取出第一個(gè)元素
return queryIsArray ? res : res[0];
});
}
調(diào)用方式如下:
// 第一次調(diào)用,向服務(wù)器發(fā)送請(qǐng)求,獲取數(shù)據(jù)
queryAll("wares").then((data) => {
console.log(data);
});
// 第二次調(diào)用,從 Map 對(duì)象中獲取 Promise 對(duì)象,不再發(fā)送請(qǐng)求,等待第一次請(qǐng)求的結(jié)果
queryAll(["wares", "skus"]).then((data) => {
console.log(data);
});
總結(jié)
通過(guò)實(shí)施API請(qǐng)求緩存,我們可以顯著提高前端應(yīng)用程序的性能和用戶體驗(yàn)。選擇合適的緩存方案取決于應(yīng)用程序的需求和特點(diǎn)。在實(shí)際開發(fā)中,我們可以根據(jù)數(shù)據(jù)的變化頻率、數(shù)據(jù)的重要性、存儲(chǔ)空間的限制等因素來(lái)選擇適當(dāng)?shù)姆桨浮=Y(jié)合合適的緩存方案,我們可以有效地減少網(wǎng)絡(luò)請(qǐng)求,提高應(yīng)用程序的響應(yīng)速度,并降低服務(wù)器負(fù)載。同時(shí),需要注意緩存的更新策略,以確保數(shù)據(jù)的準(zhǔn)確性和實(shí)時(shí)性。
如果你對(duì)前端工程師職業(yè)和編程技術(shù)感興趣,不妨訪問(wèn)編程獅官網(wǎng)(http://m.hgci.cn/)。編程獅官網(wǎng)提供了大量的技術(shù)文章、編程教程和資源,涵蓋了前端工程師、編程、職業(yè)規(guī)劃等多個(gè)領(lǐng)域的知識(shí)。無(wú)論你是初學(xué)者還是有經(jīng)驗(yàn)的開發(fā)者,編程獅官網(wǎng)都為你提供了有用的信息和資源,助你在編程領(lǐng)域取得成功。不要錯(cuò)過(guò)這個(gè)寶貴的學(xué)習(xí)機(jī)會(huì)!