網頁儲存空間

您可以透過多種方式在瀏覽器中儲存資料。哪一個最符合您的需求?

網際網路連線可能不穩定或無法在行動裝置上使用,因此漸進式網頁應用程式常見的功能包括離線支援和可靠的效能。即使在完美的無線環境中,謹慎使用快取和其他儲存技術也能大幅改善使用者體驗。您可以透過多種方式快取靜態應用程式資源 (HTML、JavaScript、CSS、圖片等) 和資料 (使用者資料、新聞文章等)。但哪個才是最佳解決方案?可以儲存多少份量?如何防止系統將其淘汰?

我該使用什麼?

以下為儲存資源的一般建議:

所有新式瀏覽器都支援 IndexedDB、OPFS 和 Cache Storage API。這些函式為非同步,不會封鎖主執行緒 (但 OPFS 也有同步變化版本,專供網路工作者使用)。這些元件可透過 window 物件、網路工作站和服務工作站存取,因此可在程式碼中的任何位置使用。

其他儲存機制呢?

瀏覽器中還有其他幾種儲存機制,但這些機制用途有限,且可能會造成嚴重的效能問題。

SessionStorage 是分頁專屬,且範圍限定於分頁的生命週期。這類資訊可用於儲存少量的工作階段專屬資訊,例如 IndexedDB 鍵。由於這項方法為同步處理,且會封鎖主執行緒,因此應謹慎使用。大小上限約為 5MB,且只能包含字串。由於這項屬性是特定於分頁,因此無法透過網路工作站或服務工作站存取。

請避免使用 LocalStorage,因為它是同步處理,且會封鎖主執行緒。大小上限約為 5 MB,且只能包含字串。網路工作站或服務工作站無法存取 LocalStorage。

Cookie 有其用途,但不應用於儲存。Cookie 會隨每個 HTTP 要求傳送,因此儲存的資料量若超過少量,每個網頁要求的大小就會大幅增加。這些事件是同步的,且無法透過網路工作者存取。與 LocalStorage 和 SessionStorage 一樣,Cookie 只能使用字串。

File System Access API 的設計目的,是讓使用者能夠讀取及編輯本機檔案系統中的檔案。使用者必須先授予權限,網頁才能讀取或寫入任何本機檔案,且除非檔案句柄已在 IndexedDB 中快取,否則權限不會在不同工作階段之間保留。File System Access API 最適合編輯器等用途,因為您需要開啟檔案、修改檔案,然後可能還要將變更儲存回檔案。

File System API 和 FileWriter API 提供方法,可讀取及寫入沙箱檔案系統中的檔案。雖然這項指令是異步的,但不建議使用,因為這項指令僅適用於以 Chromium 為基礎的瀏覽器

我可以儲存多少資料?

簡單來說,是指「很多」,至少數百 MB,甚至可能數百 GB 以上。瀏覽器實作方式各有不同,但可用的儲存空間量通常取決於裝置的可用儲存空間量。

  • Chrome 最多允許瀏覽器使用 80% 的總磁碟空間。來源最多可使用 60% 的總磁碟空間。您可以使用 StorageManager API 判斷可用的配額上限。其他以 Chromium 為基礎的瀏覽器可能會有所不同。
    • 在無痕模式下,Chrome 會將來源可使用的儲存空間量減少至總磁碟空間的約 5%。
    • 如果使用者在 Chrome 中啟用了「關閉所有視窗時清除 Cookie 和網站資料」,儲存空間配額會大幅減少至約 300 MB 。
  • Firefox 允許瀏覽器使用最多 50% 的可用磁碟空間。eTLD+1 群組 (例如example.comwww.example.comfoo.bar.example.com) 最多可使用 2 GB。您可以使用 StorageManager API 來判斷還有多少可用空間。
  • Safari (電腦版和行動版) 似乎允許大約 1 GB。達到上限時,Safari 會提示使用者,並以 200 MB 的增量增加上限。我找不到任何官方文件。
    • 如果將 PWA 新增至行動版 Safari 的主畫面,系統會建立新的儲存空間容器,且 PWA 和行動版 Safari 之間不會共用任何內容。如果已安裝的 PWA 達到配額,就無法要求額外儲存空間。

過去,如果網站儲存的資料超過特定門檻,瀏覽器就會提示使用者授權使用更多資料。舉例來說,如果來源使用超過 50 MB,瀏覽器會提示使用者允許儲存最多 100 MB,然後以 50 MB 為單位再次要求。

目前,大多數新式瀏覽器都不會向使用者發出提示,並允許網站使用分配的配額。Safari 顯示為例外狀況,會在超過儲存空間配額時出現提示,要求提高分配配額的權限。如果來源嘗試使用超過配額的配額,後續寫入資料的作業就會失敗。

如何查看可用的儲存空間?

許多瀏覽器中,您可以使用 StorageManager API 判斷來源可用的儲存空間量,以及來源使用的儲存空間量。它會回報 IndexedDB 和 Cache API 使用的位元組總數,進而計算剩餘的可用儲存空間。

if (navigator.storage && navigator.storage.estimate) {
  const quota = await navigator.storage.estimate();
  // quota.usage -> Number of bytes used.
  // quota.quota -> Maximum number of bytes available.
  const percentageUsed = (quota.usage / quota.quota) * 100;
  console.log(`You've used ${percentageUsed}% of the available storage.`);
  const remaining = quota.quota - quota.usage;
  console.log(`You can write up to ${remaining} more bytes.`);
}

您必須擷取超出配額的錯誤 (請見下文)。在某些情況下,可用配額可能會超過可用的實際儲存空間數量。

檢查

開發期間,您可以使用瀏覽器的 DevTools 檢查不同的儲存類型,並清除所有儲存的資料。

Chrome 88 新增一項功能,可讓您在「儲存空間」窗格中覆寫網站的儲存空間配額。這項功能可讓您模擬不同的裝置,並在磁碟可用性低的情況下測試應用程式的行為。依序前往「應用程式」和「儲存空間」,啟用「模擬自訂儲存空間配額」核取方塊,然後輸入任何有效的數字來模擬儲存空間配額。

在撰寫本指南時,我撰寫了一個簡單工具,嘗試盡可能快速使用最多儲存空間。這是快速嘗試不同儲存空間機制的最佳方式,您可以藉此瞭解使用所有配額時會發生什麼情況。

如何處理超出配額的情況?

超出配額時該怎麼辦?最重要的是,無論是 QuotaExceededError 或其他項目,您都應隨時擷取並處理寫入錯誤。接著,根據應用程式設計決定處理方式。例如刪除長時間未存取的內容、根據大小移除資料,或讓使用者選擇想刪除的內容。

如果超過可用配額,IndexedDB 和 Cache API 都會擲回名為 QuotaExceededErrorDOMError

IndexedDB

如果來源已超過配額,嘗試寫入 IndexedDB 的動作就會失敗。系統會呼叫交易的 onabort() 處理常式,並傳遞事件。事件會在錯誤屬性中加入 DOMException。檢查錯誤 name 會傳回 QuotaExceededError

const transaction = idb.transaction(['entries'], 'readwrite');
transaction.onabort = function(event) {
  const error = event.target.error; // DOMException
  if (error.name == 'QuotaExceededError') {
    // Fallback code goes here
  }
};

Cache API

如果來源已超出配額,嘗試寫入 Cache API 時會傳回 QuotaExceededError DOMException 並遭到拒絕。

try {
  const cache = await caches.open('my-cache');
  await cache.add(new Request('/sample1.jpg'));
} catch (err) {
  if (error.name === 'QuotaExceededError') {
    // Fallback code goes here
  }
}

排除功能的運作原理為何?

網路儲存空間分為兩個類別:「盡力而為」和「持續性」。盡可能確保瀏覽器可以在不中斷使用者的情況下清除儲存空間,但對於長期或重要資料較不耐用。儲存空間不足時,系統不會自動清除永久性儲存空間。使用者必須手動清除這類儲存空間 (透過瀏覽器設定)。

根據預設,網站資料 (包括 IndexedDB、Cache API 等) 會歸類為盡力保留類別,也就是說,除非網站已要求持續性儲存空間,否則瀏覽器可能會視情況淘汰網站資料,例如在裝置儲存空間不足時。

盡力保證的淘汰政策如下:

  • 當瀏覽器空間不足時,以 Chromium 為基礎的瀏覽器就會開始清除資料,首先清除最近最少使用的來源的所有網站資料,然後再清除下一個來源的資料,直到瀏覽器不再超出限制為止。
  • 當可用磁碟空間用盡時,Firefox 就會開始清除資料,首先清除最近最少使用的來源的所有網站資料,然後依序清除其他來源的資料,直到瀏覽器不再超出限制為止。
  • Safari 先前不會移除資料,但最近已針對所有可寫入的儲存空間實施新的七天上限 (請參閱下文)。

從 iOS 和 iPadOS 13.4 以及 macOS 的 Safari 13.1 開始,所有指令碼可寫入的儲存空間均設有七天上限,包括 IndexedDB、服務工作站註冊和 Cache API。這表示如果使用者未在使用 Safari 七天後與網站互動,Safari 就會從快取中移除所有內容。這項淘汰政策不適用於已新增至主畫面的已安裝 PWA。如需完整詳細資料,請參閱 WebKit 網誌上的「全面封鎖第三方 Cookie 和其他功能」。

Storage Bucket

Storage Buckets API 的核心概念是授予網站建立多個儲存空間值區的功能,可供瀏覽器選擇單獨刪除每個值區,不與其他值區分開。這可讓開發人員指定淘汰優先順序,確保最有價值的資料不會遭到刪除。

額外說明:為什麼要使用 IndexedDB 的包裝函式

IndexedDB 是低階 API,且在使用之前需要進行大量設定,這在儲存複雜性資料時會特別困難。與大多數以承諾為基礎的現代 API 不同,這個 API 是以事件為基礎。idb 等 IndexedDB 的應許包裝函式會隱藏部分強大功能,但更重要的是,隱藏 IndexedDB 程式庫隨附的複雜機制 (例如交易、結構定義版本)。

額外內容:SQLite Wasm

在 Web SQL 淘汰並從 Chrome 中移除後,Google 與熱門 SQLite 資料庫的維護人員合作,提供以 SQLite 為基礎的 Web SQL 替代方案。請參閱「由來源私人檔案系統支援的瀏覽器中的 SQLite Wasm」一文。

結論

不必再使用有限的儲存空間,並提示使用者儲存更多資料。網站可以有效儲存所有執行所需的資源和資料。您可以使用 StorageManager API 判斷可用空間量,以及您已使用的空間量。使用持續性儲存空間時,除非使用者移除,否則您可以保護資料,避免資料遭到淘汰。

其他資源

謝謝

特別感謝 Jarryd Goodman、Phil Walton、Eiji Kitamura、Daniel Murphy、Darwin Huang、Josh Bell、Marijn Kruisselbrink 和 Victor Costan 審查本指南。感謝 Eiji Kitamura、Addy Osmani 和 Marc Cohen 提供以此為基礎撰寫的原始文章。Eiji 撰寫了名為 Browser Storage Abuser 的實用工具,可用於驗證目前的行為。可讓您盡量儲存資料,並查看瀏覽器的儲存空間限制。感謝 François Beaufort 深入研究 Safari 以找出其儲存空間限制,以及 Thomas Steiner 提供有關來源私人檔案系統、儲存值區、SQLite Wasm 的資訊,以及 2024 年的整體內容更新。

主頁橫幅圖片由 Guillaume Bolduc 提供,並發布於 Unsplash 網站。