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。请编写不会阻塞界面的代码。

  • 安全性注意事项 不要将访问令牌等敏感信息存储在 localStorage 中。如有可能,请考虑使用 HttpOnly Cookie 或对数据加密。

  • 后备兼容设计 如果 supportsStorageManager() 返回 false,请切换为只使用 localStorageIndexedDB 的逻辑。

总结

通过使用 supportsStorageManager() 进行功能检测,可以安全地兼容各种环境,用 navigator.storage.estimate() 检查用量和配额,并用 persist() 请求数据持久化。在 TypeScript 中,使用封装函数和类型定义来编写可读且安全的代码。基本流程为:检测是否存在、获取估算值、请求持久化,并在写入存储前检查容量。

您可以在我们的YouTube频道上使用Visual Studio Code跟随上述文章进行学习。 请也查看我们的YouTube频道。

YouTube Video