首頁 > web前端 > js教程 > 緩存在本地提取Ajax請求:包裝Fetch API

緩存在本地提取Ajax請求:包裝Fetch API

Lisa Kudrow
發布: 2025-02-17 11:06:10
原創
521 人瀏覽過

Cache Fetched AJAX Requests Locally: Wrapping the Fetch API

本文由特邀作者Peter Bengtsson撰寫。 SitePoint特邀文章旨在為您帶來來自JavaScript社區知名作家和演講者的精彩內容

本文演示瞭如何實現已提取請求的本地緩存,以便如果重複執行,則從會話存儲中讀取。這樣做的好處是,您無需為要緩存的每個資源編寫自定義代碼。

如果您想在下次JavaScript聚會上炫耀一番,展示您在處理Promise、最先進的API和本地存儲方面的各種技能,請繼續閱讀。

主要收穫

  • 利用Fetch API,開發人員可以創建AJAX請求的本地緩存,通過減少冗餘的網絡調用和加快數據檢索來提高效率。
  • 使用全局變量進行緩存的簡單方法受會話持久性的限制;切換到會話存儲允許數據在同一會話中跨頁面重新加載持久存在。
  • 實現cachedFetch封裝了標準的fetch調用,可以根據內容類型和URL自動緩存響應,從而使緩存機制通用化。
  • cachedFetch的增強功能包括在進行網絡請求之前處理來自會話存儲的緩存命中,以及管理內容過期以避免使用過時數據。
  • 未來的改進可能包括處理二進制數據和使用哈希URL作為緩存鍵,以優化Web應用程序中的存儲和檢索過程。

Fetch API

此時,您應該熟悉fetch。它是瀏覽器中一個新的原生API,用於替換舊的XMLHttpRequest API。

Can I Use fetch? https://www.php.cn/link/b751ea087892ebeca363034301f45c69網站上關於主要瀏覽器對fetch功能支持的數據。

在並非所有瀏覽器都完美實現的地方,您可以使用GitHub的fetch polyfill(如果您整天無所事事,這裡有Fetch標準規範)。

簡單的替代方案

假設您確切知道需要下載哪個資源,並且只想下載一次。您可以使用全局變量作為緩存,如下所示:

let origin = null;
fetch('https://httpbin.org/get')
  .then(r => r.json())
  .then(information => {
    origin = information.origin; // 您的客户端IP
  });

// 需要延迟以确保fetch已完成
setTimeout(() => {
  console.log('您的来源是 ' + origin);
}, 3000);
登入後複製
登入後複製
登入後複製
登入後複製

CodePen示例

這僅僅依賴於全局變量來保存緩存的數據。直接的問題是,如果您重新加載頁面或導航到新頁面,緩存的數據就會消失。

在我們剖析其缺點之前,讓我們升級一下第一個簡單的解決方案。

fetch('https://httpbin.org/get')
  .then(r => r.json())
  .then(info => {
    sessionStorage.setItem('information', JSON.stringify(info));
  });

// 需要延迟以确保fetch已完成
setTimeout(() => {
  let info = JSON.parse(sessionStorage.getItem('information'));
  console.log('您的来源是 ' + info.origin);
}, 3000);
登入後複製
登入後複製
登入後複製

CodePen示例

第一個直接的問題是fetch是基於Promise的,這意味著我們無法確定它何時完成,因此為了確定起見,我們不應依賴於它的執行,直到它的Promise解析。

第二個問題是此解決方案非常特定於特定的URL和特定的緩存數據片段(在此示例中為關鍵信息)。我們想要的是一個基於URL的通用解決方案。

第一次實現 – 保持簡單

讓我們圍繞fetch創建一個包裝器,它也返回一個Promise。調用它的代碼可能並不關心結果是來自網絡還是來自本地緩存。

所以想像一下您曾經這樣做:

let origin = null;
fetch('https://httpbin.org/get')
  .then(r => r.json())
  .then(information => {
    origin = information.origin; // 您的客户端IP
  });

// 需要延迟以确保fetch已完成
setTimeout(() => {
  console.log('您的来源是 ' + origin);
}, 3000);
登入後複製
登入後複製
登入後複製
登入後複製

CodePen示例

現在您想對其進行包裝,以便重複的網絡調用可以從本地緩存中獲益。讓我們簡單地將其稱為cachedFetch,因此代碼如下所示:

fetch('https://httpbin.org/get')
  .then(r => r.json())
  .then(info => {
    sessionStorage.setItem('information', JSON.stringify(info));
  });

// 需要延迟以确保fetch已完成
setTimeout(() => {
  let info = JSON.parse(sessionStorage.getItem('information'));
  console.log('您的来源是 ' + info.origin);
}, 3000);
登入後複製
登入後複製
登入後複製

第一次運行時,它需要通過網絡解析請求並將結果存儲在緩存中。第二次應該直接從本地存儲中提取。

讓我們從簡單地包裝fetch函數的代碼開始:

fetch('https://httpbin.org/get')
  .then(r => r.json())
  .then(issues => {
    console.log('您的来源是 ' + info.origin);
  });
登入後複製
登入後複製

CodePen示例

這可以工作,但當然沒用。讓我們首先實現存儲提取的數據。

cachedFetch('https://httpbin.org/get')
  .then(r => r.json())
  .then(info => {
    console.log('您的来源是 ' + info.origin);
  });
登入後複製

CodePen示例

這裡有很多事情要做。

fetch返回的第一個Promise實際上會繼續執行GET請求。如果CORS(跨源資源共享)有問題,.text()、.json()或.blob()方法將無法工作。

最有趣的功能是,我們必須克隆第一個Promise返回的Response對象。如果我們不這樣做,我們就會過度注入自己,當Promise的最終用戶嘗試調用.json()(例如)時,他們會收到此錯誤:

const cachedFetch = (url, options) => {
  return fetch(url, options);
};
登入後複製

需要注意的另一件事是對響應類型的仔細處理:我們只在狀態碼為200 並且內容類型為application/json或text/*時才存儲響應。這是因為sessionStorage只能存儲文本。

以下是如何使用它的示例:

const cachedFetch = (url, options) => {
  // 使用URL作为sessionStorage的缓存键
  let cacheKey = url;
  return fetch(url, options).then(response => {
    // 让我们只在内容类型为JSON或非二进制内容时存储在缓存中
    let ct = response.headers.get('Content-Type');
    if (ct && (ct.match(/application\/json/i) || ct.match(/text\//i))) {
      // 有一个.json()而不是.text(),但我们将它存储在sessionStorage中作为字符串。
      // 如果我们不克隆响应,它将在返回时被使用。这样我们就可以不干扰。
      response.clone().text().then(content => {
        sessionStorage.setItem(cacheKey, content);
      });
    }
    return response;
  });
};
登入後複製

到目前為止,這個解決方案的巧妙之處在於它可以工作,而且不會干擾JSONHTML請求。當它是圖像時,它不會嘗試將其存儲在sessionStorage中。

第二次實現 – 實際返回緩存命中

因此,我們的第一次實現只是負責存儲請求的響應。但是,如果您第二次調用cachedFetch,它仍然不會嘗試從sessionStorage檢索任何內容。我們需要做的首先是返回一個Promise,並且Promise需要解析一個Response對象。

讓我們從一個非常基本的實現開始:

<code>TypeError: Body has already been consumed.</code>
登入後複製

CodePen示例

它可以工作!

要查看它的實際效果,請打開此代碼的CodePen,然後在開發者工具中打開瀏覽器的“網絡”選項卡。按幾次“運行”按鈕(CodePen的右上角),您應該會看到只有圖像正在重複通過網絡請求。

此解決方案的一個巧妙之處在於缺乏“回調意大利面”。由於sessionStorage.getItem調用是同步的(即阻塞的),我們不必在Promise或回調中處理“它是否在本地存儲中?”。並且只有在有內容的情況下,我們才會返回緩存的結果。如果沒有,if語句只會繼續執行常規代碼。

第三次實現 – 過期時間呢?

到目前為止,我們一直在使用sessionStorage,它就像localStorage一樣,只是sessionStorage在您啟動新選項卡時會被清除。這意味著我們正在利用一種“自然方式”來避免緩存時間過長。如果我們改用localStorage並緩存某些內容,即使遠程內容已更改,它也會永遠卡在那裡,這很糟糕。

更好的解決方案是讓用戶控制。 (在這種情況下,用戶是使用我們的cachedFetch函數的Web開發人員)。就像服務器端的Memcached或Redis存儲一樣,您可以設置一個生存期,指定應緩存多長時間。

例如,在Python(使用Flask)中:

let origin = null;
fetch('https://httpbin.org/get')
  .then(r => r.json())
  .then(information => {
    origin = information.origin; // 您的客户端IP
  });

// 需要延迟以确保fetch已完成
setTimeout(() => {
  console.log('您的来源是 ' + origin);
}, 3000);
登入後複製
登入後複製
登入後複製
登入後複製

現在,sessionStorage和localStorage都沒有內置此功能,因此我們必須手動實現它。我們將通過始終記錄存儲時間的時間戳來做到這一點,並使用它來比較可能的緩存命中。

但在我們這樣做之前,它會是什麼樣子?比如這樣:

fetch('https://httpbin.org/get')
  .then(r => r.json())
  .then(info => {
    sessionStorage.setItem('information', JSON.stringify(info));
  });

// 需要延迟以确保fetch已完成
setTimeout(() => {
  let info = JSON.parse(sessionStorage.getItem('information'));
  console.log('您的来源是 ' + info.origin);
}, 3000);
登入後複製
登入後複製
登入後複製

我們將添加的關鍵新內容是,每次保存響應數據時,我們會記錄何時存儲它。但請注意,現在我們也可以切換到localStorage的更可靠存儲,而不是sessionStorage。我們的自定義過期代碼將確保我們不會在持久性localStorage中獲得非常陳舊的緩存命中。

所以這是我們最終的工作解決方案:

fetch('https://httpbin.org/get')
  .then(r => r.json())
  .then(issues => {
    console.log('您的来源是 ' + info.origin);
  });
登入後複製
登入後複製

CodePen示例

未來的實現 – 更好、更花哨、更酷

我們不僅避免了過度訪問這些Web API,最好的部分是localStorage比依賴網絡快得多。請參閱這篇博文以了解localStorage與XHR的比較:localForage vs. XHR。它測量其他內容,但基本上得出結論,localStorage非常快,磁盤緩存預熱很少見。

那麼我們如何進一步改進我們的解決方案呢?

處理二進制響應

我們這裡的實現不會緩存非文本內容(如圖像),但沒有理由不能緩存。我們需要更多代碼。特別是,我們可能想要存儲更多關於Blob的信息。每個響應基本上都是一個Blob。對於文本和JSON,它只是一個字符串數組。類型和大小並不重要,因為您可以從字符串本身推斷出來。對於二進制內容,blob必須轉換為ArrayBuffer。

對於好奇的人,要查看支持圖像的實現擴展,請查看此CodePen:[https://www.php.cn/link/946af3555203afdb63e571b873e419f6]。

使用哈希緩存鍵

另一個潛在的改進是通過對每個URL(我們用作鍵)進行哈希處理來用空間換取速度,使其變得更小。在上面的示例中,我們只使用了一些非常小巧簡潔的URL(例如https://httpbin.org/get),但是如果您有非常長的URL,有很多查詢字符串內容,並且有很多這樣的URL,那麼它們加起來就會非常多。

解決這個問題的方法是使用這種巧妙的算法,它被認為是安全且快速的:

let origin = null;
fetch('https://httpbin.org/get')
  .then(r => r.json())
  .then(information => {
    origin = information.origin; // 您的客户端IP
  });

// 需要延迟以确保fetch已完成
setTimeout(() => {
  console.log('您的来源是 ' + origin);
}, 3000);
登入後複製
登入後複製
登入後複製
登入後複製

如果您喜歡這個,請查看此CodePen:[https://www.php.cn/link/946af3555203afdb63e571b873e419f6]。如果您在Web控制台中檢查存儲,您會看到類似於557027443的鍵。

結論

您現在有一個可以添加到Web應用程序中的工作解決方案,在該解決方案中,您可能正在使用Web API,並且您知道響應可以很好地為您的用戶緩存。

最後一件事可能是此原型的自然擴展,即將其超越文章,進入一個真實的、具體的項目,帶有測試和自述文件,並在npm上發布它——但這留待以後再說!

關於緩存已提取AJAX請求的常見問題解答 (FAQ)

緩存已提取AJAX請求的重要性是什麼?

緩存已提取的AJAX請求對於提高Web應用程序的性能至關重要。它允許瀏覽器存儲服務器響應的副本,以便它不必再次發出相同的請求。這減少了服務器的負載,並加快了網頁的加載時間,從而提供了更好的用戶體驗。

Fetch API如何與緩存一起工作?

Fetch API提供了一種強大且靈活的方法來發出HTTP請求。它包含一個內置的緩存機制,允許您指定請求應如何與緩存交互。您可以將緩存模式設置為“default”、“no-store”、“reload”、“no-cache”、“force-cache”或“only-if-cached”,每種模式都提供不同級別的緩存控制。

Fetch API中有哪些不同的緩存模式,它們是什麼意思?

Fetch API提供了幾種緩存模式。 “default”遵循標準的HTTP緩存規則。 “no-store”完全繞過緩存。 “reload”忽略任何緩存數據並發送新的請求。 “no-cache”在使用緩存版本之前使用服務器驗證數據。 “force-cache”無論其新鮮度如何都使用緩存數據。 “only-if-cached”僅在緩存數據可用時才使用它,否則失敗。

如何在AJAX請求中實現緩存?

您可以通過在AJAX設置中設置cache屬性來在AJAX請求中實現緩存。如果設置為true,它將允許瀏覽器緩存響應。或者,您可以使用Fetch API的緩存選項來更好地控制緩存的行為。

如何防止AJAX請求中的緩存?

要防止AJAX請求中的緩存,您可以將AJAX設置中的cache屬性設置為false。這將強制瀏覽器不將其響應存儲在其緩存中。或者,您可以使用Fetch API的“no-store”緩存選項來完全繞過緩存。

AJAX和Fetch API中的緩存有什麼區別?

雖然AJAX和Fetch API都提供了緩存機制,但Fetch API提供了更大的靈活性和控制性。 AJAX的cache屬性是一個簡單的布爾值,它允許或不允許緩存。另一方面,Fetch API的緩存選項允許您指定請求應如何與緩存交互,從而為您提供更細粒度的控制。

緩存如何影響我的Web應用程序的性能?

緩存可以顯著提高Web應用程序的性能。通過存儲服務器響應的副本,瀏覽器不必再次發出相同的請求。這減少了服務器的負載,並加快了網頁的加載時間。但是,必須正確管理緩存,以確保您的用戶看到最新的內容。

我可以控制單個AJAX請求的緩存行為嗎?

是的,您可以通過為每個請求在AJAX設置中設置cache屬性來控制單個AJAX請求的緩存行為。這允許您指定瀏覽器是否應該緩存響應。

如何清除AJAX請求的緩存?

清除AJAX請求的緩存可以通過在AJAX設置中將cache屬性設置為false來完成。這將強制瀏覽器不將其響應存儲在其緩存中。或者,您可以使用Fetch API的“reload”緩存選項來忽略任何緩存數據並發送新的請求。

緩存AJAX請求的一些最佳實踐是什麼?

緩存AJAX請求的一些最佳實踐包括:了解不同的緩存模式以及何時使用它們,正確管理緩存以確保用戶看到最新的內容,以及使用Fetch API的緩存選項來更好地控制緩存。在決定緩存策略時,還必須考慮數據的性質和用戶體驗。

以上是緩存在本地提取Ajax請求:包裝Fetch API的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
作者最新文章
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板