網頁儲存空間

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

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

我該使用哪一種?

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

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

其他儲存機制呢?

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

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、服務 worker 註冊和 Cache API) 都設有七天的上限。也就是說,如果使用者未與網站互動,Safari 會在使用七天後將快取中的所有內容清除。這項淘汰政策不適用於已新增至主畫面的已安裝 PWA。如需完整詳細資料,請參閱 WebKit 網誌上的「全面封鎖第三方 Cookie 和其他功能」。

Storage Bucket

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

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

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

額外內容:SQLite Wasm

在 Web SQL 淘汰並從 Chrome 中移除後,Google 與熱門 SQLite 資料庫的維護人員合作,提供以 SQLite 為基礎的 Web SQL 替代方案。請參閱「在 Origin 私人檔案系統支援的瀏覽器中使用 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 年的整體內容更新。

主頁橫幅圖片出自 Unsplash 上的 Guillaume Bolduc。