瀏覽器提供多種資料儲存選項。哪一個最符合您的需求?
行動裝置的網路連線可能不穩定或無法連線,因此漸進式網頁應用程式通常會提供離線支援和穩定效能。即使在無線環境良好時,善用快取和其他儲存技術,也能大幅提升使用者體驗。您可以透過多種方式快取靜態應用程式資源 (HTML、JavaScript、CSS、圖片等) 和資料 (使用者資料、新聞文章等)。但哪種解決方案最適合?How much can you store? 如何避免遭到驅逐?
我該使用哪一個?
以下是儲存資源的一般建議:
- 如要載入應用程式所需的網路資源,請使用 Cache Storage API (屬於Service Worker)。
- 如果是以檔案為基礎的內容,請使用來源私人檔案系統 (OPFS)。
- 如要儲存其他資料,請使用 IndexedDB (搭配Promise 包裝函式)。
所有新式瀏覽器都支援 IndexedDB、OPFS 和 Cache Storage API。這些作業是非同步作業,不會封鎖主執行緒 (但 OPFS 也有同步變體,僅適用於網頁工作站)。這些 API 可從 window 物件、網頁工作人員和服務工作人員存取,因此可在程式碼中的任何位置使用。
其他儲存機制呢?
瀏覽器中還有其他幾種儲存機制,但用途有限,而且可能會導致嚴重的效能問題。
SessionStorage 專屬於分頁,且範圍限定於分頁的生命週期。這項功能可用於儲存少量工作階段專屬資訊,例如 IndexedDB 金鑰。由於這是同步作業,會封鎖主執行緒,因此使用時請務必謹慎。大小上限約為 5 MB,且只能包含字串。由於這項功能專屬於分頁,因此無法從 Web Worker 或 Service Worker 存取。
請避免使用 LocalStorage,因為它是同步性質,會封鎖主執行緒。大小上限約為 5 MB,且只能包含字串。網路工作站或服務工作站無法存取 LocalStorage。
Cookie 有其用途,但不應做為儲存空間。 Cookie 會隨每個 HTTP 要求傳送,因此如果儲存的資料量超過一小部分,每個網頁要求的大小就會大幅增加。這些 API 是同步的,且無法從網頁工作人員存取。與 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.com、www.example.com和foo.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 判斷來源可用的儲存空間量,以及來源使用的儲存空間量。這項 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.`);
}
您必須擷取超出配額的錯誤 (請參閱下文)。在某些情況下,可用配額可能會超過實際可用的儲存空間量。
檢查
開發期間,您可以使用瀏覽器的開發人員工具檢查不同類型的儲存空間,並清除所有儲存的資料。
Chrome 88 新增了一項功能,可讓您在「儲存空間」窗格中覆寫網站的儲存空間配額。這項功能可讓您模擬不同裝置,並在磁碟空間不足的情況下測試應用程式的行為。依序前往「Application」和「Storage」,啟用「Simulate custom storage quota」核取方塊,然後輸入任何有效數字,模擬儲存空間配額。
撰寫本指南時,我編寫了一個簡單工具,盡可能快速使用大量儲存空間。您可以快速試驗不同的儲存機制,並瞭解用盡配額時會發生什麼情況。
如何處理超出配額的情況?
如果超出配額,該怎麼辦?最重要的是,您應一律擷取並處理寫入錯誤,無論是 QuotaExceededError 或其他錯誤。然後根據應用程式設計,決定如何處理。
例如刪除長期未存取的內容、根據大小移除資料,或提供使用者選擇要刪除的內容。
當您超出可用配額時,IndexedDB 和 Cache API 都會擲回 DOMError 名為 QuotaExceededError 的例外狀況。
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 會在七天後清除快取中的所有內容。這項逐出政策不適用於已安裝並新增至主畫面的 PWA。如需完整詳細資料,請參閱 WebKit 網誌的「Full Third-Party Cookie Blocking and More」(全面封鎖第三方 Cookie 等等)。
Storage bucket
儲存空間值區 API 的核心概念是授予網站建立多個儲存空間值區的權限,瀏覽器可能會選擇獨立刪除每個值區。開發人員可以指定清除優先順序,確保不會刪除最有價值的資料。
加碼:為什麼要使用 IndexedDB 的包裝函式
IndexedDB 是低階 API,使用前需要大量設定,儲存低複雜度資料時尤其麻煩。與大多數現代的 Promise 型 API 不同,這個 API 是以事件為基礎。IndexedDB 的 Promise 包裝函式 (例如 idb) 會隱藏部分強大功能,但更重要的是,會隱藏 IndexedDB 程式庫隨附的複雜機制 (例如交易、結構定義版本控管)。
加分題:SQLite Wasm
Web SQL 淘汰並從 Chrome 移除後,Google 與熱門 SQLite 資料庫的維護人員合作,以 SQLite 為基礎提供 Web SQL 的替代方案。如要瞭解如何使用這項功能,請參閱「SQLite Wasm in the browser backed by the Origin Private File System」。
結論
過去儲存空間有限,因此系統會不斷提示使用者儲存更多資料。網站可有效儲存執行所需的所有資源和資料。您可以使用 StorageManager API 判斷可用的儲存空間大小,以及已使用的儲存空間大小。此外,您可以使用永久儲存空間,保護儲存空間不遭逐出,除非使用者移除。
其他資源
謝謝
特別感謝 Jarryd Goodman、Phil Walton、北村英二、Daniel Murphy、黃達文、Josh Bell、Marijn Kruisselbrink 和 Victor Costan 審查本指南。感謝 Eiji Kitamura、Addy Osmani 和 Marc Cohen 撰寫原始文章,本文即是以這些文章為基礎。Eiji 編寫了一項實用工具,名為「Browser Storage Abuser」,可用於驗證目前的行為。可盡可能儲存資料,並在瀏覽器中查看儲存空間限制。感謝 François Beaufort 深入研究 Safari,找出其儲存空間限制;也感謝 Thomas Steiner 在 2024 年新增來源私有檔案系統、儲存空間 bucket、SQLite Wasm 的相關資訊,並全面更新內容。