TypeScript 與 StorageManager

TypeScript 與 StorageManager

本文將解釋 TypeScript 與 StorageManager。

我們提供實用範例來說明 TypeScript 與 StorageManager。

YouTube Video

TypeScript 與 StorageManager

什麼是 StorageManager?

StorageManager 是一個 API,讓網頁應用程式能預估目前使用了多少儲存空間,以及還有多少可用容量。它也提供持久化請求(persist()),以防止用戶或瀏覽器自動刪除資料。這能讓你控制是否有足夠的剩餘空間,或資料是否會自動被刪除,例如在 IndexedDB 儲存大量離線應用數據時。

功能檢測

首先,請檢查瀏覽器是否支援 navigator.storage

 1// TypeScript: feature detection for StorageManager
 2function supportsStorageManager(): boolean {
 3  return typeof navigator !== "undefined" &&
 4         typeof navigator.storage !== "undefined" &&
 5         typeof navigator.storage.estimate === "function";
 6}
 7
 8// Example usage
 9if (supportsStorageManager()) {
10  console.log("StorageManager is supported.");
11} else {
12  console.warn("StorageManager not supported. Falling back to localStorage or IndexedDB.");
13}
  • 這個函式會分階段檢查 StorageManager API 的可用性。
  • 由於在某些瀏覽器(如 Safari)中不存在 navigator.storage,因此使用 typeof 安全地檢查其存在性非常重要。

估算儲存用量(estimate()

在使用 supportsStorageManager() 檢查功能後,可透過 navigator.storage.estimate() 取得使用量(usage)和配額(quota)。

 1// TypeScript: safely get storage estimate
 2async function getStorageEstimate(): Promise<{ usage: number; quota: number } | null> {
 3  if (!supportsStorageManager()) {
 4    console.warn("StorageManager not supported.");
 5    return null;
 6  }
 7
 8  const estimate = await navigator.storage.estimate();
 9  return { usage: estimate.usage ?? 0, quota: estimate.quota ?? 0 };
10}
11
12// Example usage
13getStorageEstimate().then(result => {
14  if (result) {
15    console.log(`Usage: ${result.usage} bytes / Quota: ${result.quota} bytes`);
16  }
17});
  • 若瀏覽器不支援,這個函式始終會安全地返回 null
  • usagequota 僅為預估值,在不同瀏覽器之間可能會有差異。

資料持久化請求(persist()

使用 persist() 來請求持久化,讓重要資料(如離線快取)不會被瀏覽器自動刪除。但在所有環境下並不一定會成功。

 1// TypeScript: safely request persistent storage
 2async function requestPersistence(): Promise<boolean> {
 3  if (!supportsStorageManager()) {
 4    console.warn("StorageManager not supported.");
 5    return false;
 6  }
 7
 8  if (typeof navigator.storage.persist !== "function") {
 9    console.warn("persist() not available in this browser.");
10    return false;
11  }
12
13  try {
14    const granted = await navigator.storage.persist();
15    return Boolean(granted);
16  } catch (err) {
17    console.error("persist() error:", err);
18    return false;
19  }
20}
21
22// Example usage
23requestPersistence().then(granted => {
24  console.log("Persistence granted?", granted);
25});
  • persist() 成功,資料將不會被瀏覽器自動刪除,除非由使用者手動操作。但根據使用者的行為或瀏覽器設定,請求仍可能被拒絕。

儲存資料前先檢查可用空間

在儲存大量資料前,請先確認可用空間,以避免寫入失敗(QuotaExceededError)。

 1// TypeScript: ensure enough space before writing
 2async function ensureSpaceAndWrite(neededBytes: number, writeFn: () => Promise<void>): Promise<boolean> {
 3  const estimate = await getStorageEstimate();
 4  if (!estimate) {
 5    console.warn("Cannot check storage space. Proceeding without validation.");
 6    await writeFn();
 7    return true;
 8  }
 9
10  const free = estimate.quota - estimate.usage;
11  if (free < neededBytes) {
12    console.warn(`Not enough space. Free: ${free} bytes, needed: ${neededBytes} bytes.`);
13    return false;
14  }
15
16  await writeFn();
17  return true;
18}
19
20// Example usage
21ensureSpaceAndWrite(10 * 1024 * 1024, async () => {
22  console.log("Saving large data...");
23});
  • 透過在儲存前檢查可用空間,可降低因容量不足導致寫入中斷的風險。

在 TypeScript 中安全地操作 localStorage 類型

localStorage 適合儲存輕量級設定資料或類似資訊。

以下類別利用泛型打造型別安全的包裝器。

 1// TypeScript: typed localStorage wrapper
 2type Serializer<T> = {
 3  serialize: (v: T) => string;
 4  deserialize: (s: string) => T;
 5};
 6
 7class TypedLocalStorage<K extends string, V> {
 8  constructor(private storage: Storage, private serializer: Serializer<V>) {}
 9
10  set(key: K, value: V): void {
11    this.storage.setItem(key, this.serializer.serialize(value));
12  }
13
14  get(key: K): V | null {
15    const raw = this.storage.getItem(key);
16    if (raw === null) return null;
17    try {
18      return this.serializer.deserialize(raw);
19    } catch {
20      return null;
21    }
22  }
23
24  remove(key: K): void {
25    this.storage.removeItem(key);
26  }
27
28  clear(): void {
29    this.storage.clear();
30  }
31}
32
33// Example usage
34const jsonSerializer: Serializer<any> = {
35  serialize: v => JSON.stringify(v),
36  deserialize: s => JSON.parse(s),
37};
38
39const appStorage = new TypedLocalStorage<'theme' | 'token', any>(localStorage, jsonSerializer);
40appStorage.set('theme', { dark: true });
41console.log(appStorage.get('theme'));
  • 這個類別有助於降低誤用型別進行存取的風險。

實務考量與最佳實踐

  • 請務必確認瀏覽器是否支援 StorageManager 由於 StorageManager 屬於新穎 API,使用前請先確認其存在。

  • 請注意儲存上限及相容性 estimate() 回傳的值會因瀏覽器不同而異,且僅為預估數字。

  • 設計時要注意非同步 API estimate()persist() 都是基於 Promise 的非同步 API。請設計程式碼,避免阻塞使用者介面。

  • 安全性考量 請勿將敏感資訊(如 access token)存於 localStorage。如有可能,請考慮使用 HttpOnly cookies 或加密的方式。

  • 退階設計supportsStorageManager() 返回 false,請設計對應機制僅採用 localStorageIndexedDB

總結

透過 supportsStorageManager() 功能偵測,可以安全地支援所有環境,並用 navigator.storage.estimate() 檢查用量與配額,再用 persist() 請求持久化。在 TypeScript 中,請利用包裝函數與類型定義來撰寫易讀且安全的程式碼。基本流程為:檢查是否存在、進行預估、發出持久化請求,以及在寫入存儲前檢查容量。

您可以在我們的 YouTube 頻道上使用 Visual Studio Code 來跟隨上述文章一起學習。 請也查看我們的 YouTube 頻道。

YouTube Video