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

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
幕後:什麼語言能力JavaScript?幕後:什麼語言能力JavaScript?Apr 28, 2025 am 12:01 AM

JavaScript在瀏覽器和Node.js環境中運行,依賴JavaScript引擎解析和執行代碼。 1)解析階段生成抽象語法樹(AST);2)編譯階段將AST轉換為字節碼或機器碼;3)執行階段執行編譯後的代碼。

Python和JavaScript的未來:趨勢和預測Python和JavaScript的未來:趨勢和預測Apr 27, 2025 am 12:21 AM

Python和JavaScript的未來趨勢包括:1.Python將鞏固在科學計算和AI領域的地位,2.JavaScript將推動Web技術發展,3.跨平台開發將成為熱門,4.性能優化將是重點。兩者都將繼續在各自領域擴展應用場景,並在性能上有更多突破。

Python vs. JavaScript:開發環境和工具Python vs. JavaScript:開發環境和工具Apr 26, 2025 am 12:09 AM

Python和JavaScript在開發環境上的選擇都很重要。 1)Python的開發環境包括PyCharm、JupyterNotebook和Anaconda,適合數據科學和快速原型開發。 2)JavaScript的開發環境包括Node.js、VSCode和Webpack,適用於前端和後端開發。根據項目需求選擇合適的工具可以提高開發效率和項目成功率。

JavaScript是用C編寫的嗎?檢查證據JavaScript是用C編寫的嗎?檢查證據Apr 25, 2025 am 12:15 AM

是的,JavaScript的引擎核心是用C語言編寫的。 1)C語言提供了高效性能和底層控制,適合JavaScript引擎的開發。 2)以V8引擎為例,其核心用C 編寫,結合了C的效率和麵向對象特性。 3)JavaScript引擎的工作原理包括解析、編譯和執行,C語言在這些過程中發揮關鍵作用。

JavaScript的角色:使網絡交互和動態JavaScript的角色:使網絡交互和動態Apr 24, 2025 am 12:12 AM

JavaScript是現代網站的核心,因為它增強了網頁的交互性和動態性。 1)它允許在不刷新頁面的情況下改變內容,2)通過DOMAPI操作網頁,3)支持複雜的交互效果如動畫和拖放,4)優化性能和最佳實踐提高用戶體驗。

C和JavaScript:連接解釋C和JavaScript:連接解釋Apr 23, 2025 am 12:07 AM

C 和JavaScript通過WebAssembly實現互操作性。 1)C 代碼編譯成WebAssembly模塊,引入到JavaScript環境中,增強計算能力。 2)在遊戲開發中,C 處理物理引擎和圖形渲染,JavaScript負責遊戲邏輯和用戶界面。

從網站到應用程序:JavaScript的不同應用從網站到應用程序:JavaScript的不同應用Apr 22, 2025 am 12:02 AM

JavaScript在網站、移動應用、桌面應用和服務器端編程中均有廣泛應用。 1)在網站開發中,JavaScript與HTML、CSS一起操作DOM,實現動態效果,並支持如jQuery、React等框架。 2)通過ReactNative和Ionic,JavaScript用於開發跨平台移動應用。 3)Electron框架使JavaScript能構建桌面應用。 4)Node.js讓JavaScript在服務器端運行,支持高並發請求。

Python vs. JavaScript:比較用例和應用程序Python vs. JavaScript:比較用例和應用程序Apr 21, 2025 am 12:01 AM

Python更適合數據科學和自動化,JavaScript更適合前端和全棧開發。 1.Python在數據科學和機器學習中表現出色,使用NumPy、Pandas等庫進行數據處理和建模。 2.Python在自動化和腳本編寫方面簡潔高效。 3.JavaScript在前端開發中不可或缺,用於構建動態網頁和單頁面應用。 4.JavaScript通過Node.js在後端開發中發揮作用,支持全棧開發。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

MantisBT

MantisBT

Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

SecLists

SecLists

SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。

mPDF

mPDF

mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),

Atom編輯器mac版下載

Atom編輯器mac版下載

最受歡迎的的開源編輯器

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)