在设备上存储数据

渐进式 Web 应用 (PWA) 提供了可靠的选项,用于在本地存储数据,使用户能够在网络连接变得不稳定或脱机时继续工作。

PWA 可通过多种方式在设备上存储数据,例如本地存储、缓存 API 或 IndexedDB。

下表介绍了不同的选项,本文的其余部分将介绍每个选项的更多详细信息和使用方案。

存储选项 说明
Web 存储 Web 存储有两种类型:会话和本地。 Web 存储可用于存储应用的前端代码中的少量数据。 数据结构为键值对,仅适用于当前应用源。 对于会话存储,当会话结束时(例如,当应用关闭时,或者当用户浏览到同一窗口或选项卡中的另一个源时),数据将被清除。本地存储一直保留,直到应用删除数据。
IndexedDB IndexedDB 是用于存储大量结构化数据的 API。 API 是异步的,可从应用的前端代码和服务辅助角色代码中使用。 使用 IndexedDB API 在客户端上存储大量结构化数据或二进制数据,例如加密的媒体对象或文件。
缓存 缓存 API 可用于管理缓存的资源。 缓存 API 是基于 Promise 的,它允许开发人员存储和检索许多 Web 资源(HTML、CSS、JavaScript、图像、JSON 等)。 通常,缓存 API 在服务辅助角色的上下文中使用,但也可用于应用的前端代码。
文件系统访问 文件系统访问 API 允许 PWA 读取用户设备上的文件和文件夹,并将更改保存回它们。

注意:请勿使用 WebSQL 或应用程序缓存。 虽然这是另外两种浏览器存储机制,但它们都已被弃用。 使用 IndexedDB,而不是 WebSQL。 使用缓存 API,而不是应用程序缓存。

Web 存储

Web 存储可用于在用户设备上存储少量字符串数据。 Web 存储的键值对系统的简单性使其易于使用。

Web 存储仅在应用的主线程中同步工作。 这意味着 Web 存储在服务辅助角色中不可用,并且大量使用 Web 存储可能会给应用程序造成性能问题。

每种类型的 Web 存储(会话和本地存储)都作为单独的数据存储进行维护,与创建它的域隔离。

  • sessionStorage 仅在会话期间保留 -例如,当浏览器处于打开状态时,这包括刷新页面的时间。
  • localStorage 一直保留,直到应用代码、用户或浏览器删除数据。

以下代码演示如何使用 localStorage,这类似于 使用方式 sessionStorage

const browserInformation = {
  name: 'Microsoft Edge',
  version: 108
};

localStorage.setItem('browser', JSON.stringify(browserInformation));

上述代码使用 setItem() 方法将 localStorage JavaScript 对象存储为 JSON 字符串,并分配一browser个等于 的键。 可以使用 方法从中 localStorage 检索信息, getItem() 如下所示:

const value = localStorage.getItem('browser');

const browserInformation = JSON.parse(value);

若要了解详细信息,请参阅 MDN 上的 Web 存储 API

IndexedDB

IndexedDB 是一种异步 API,用于存储可在应用的前端代码或服务辅助角色代码中使用的结构化数据。 使用 IndexedDB API 在客户端上存储大量结构化数据或二进制数据,例如加密的媒体对象或文件。

IndexedDB 是在 PWA 中存储数据的最佳选择,因为使用 API 不会通过阻止主线程来减慢应用的速度,并且可以从应用的前端代码和服务辅助角色使用它。

使用 IndexedDB 比使用 Web 存储更复杂,需要执行以下步骤来存储数据:

  1. 使用 window.indexedDB.open() 函数打开数据库。
  2. 使用 IDBDatabase.createObjectStore() 函数在数据库中创建对象存储。
  3. 使用 IDBDatabase.transaction() 函数启动用于存储数据的事务。
  4. 通过侦听事件等待操作完成。

若要了解详细信息并查看代码示例,请参阅在 MDN 上使用 IndexedDB

缓存

缓存 API 是一个系统,用于在应用的前端代码或服务辅助角色中存储和检索网络请求和响应。 它可用于在用户设备上本地存储资产,例如图像和文件。 这样,即使应用程序处于脱机状态,也可以通过减少呈现应用所需的网络请求数来提高应用程序的性能。

以下代码片段演示如何侦 fetch 听服务辅助角色中的 事件,并使用缓存 API 存储来自服务器的响应:

self.addEventListener("fetch", event => {
  async function cacheAndReturnRequest() {
    // Get the response from the server.
    const fetchResponse = await fetch(event.request.url);
    // Open the app's cache.
    const cache = await caches.open("cache-name");
    // Put the response in cache.
    cache.put(event.request.url, fetchResponse.clone());
    // And return the response.
    return fetchResponse.
  }

  event.respondWith(cacheAndReturnRequest());
});

若要发现其他有用的缓存 API 方案,请参阅 使用服务辅助角色管理网络请求

文件系统访问

文件系统访问 API 使应用能够以类似于本机应用程序的方式访问用户设备上的文件。 它可用于创建可读取和写入文件的应用程序,例如文本或图像编辑器。

若要从用户设备打开文件,请使用 showOpenFilePicker() 函数:

openFileButton.addEventListener("click", async () => {
  const fileHandles = await window.showOpenFilePicker();
});

若要了解详细信息,请参阅 MDN 上的 Window.showOpenFilePicker ()

文件系统访问 API 还可以与 PWA 文件处理功能结合使用,以将应用注册为特定文件类型的处理程序,因此用户感觉更原生。 若要了解详细信息,请参阅 在渐进式 Web 应用中处理文件

源专用文件系统访问 API 是文件系统访问 API 的变体,旨在为用户提供更多隐私。 它允许应用程序访问用户设备上的文件,但只能在特定于应用源的特定目录中访问文件。 此外,此 API 并非旨在让用户使用其文件资源管理器轻松访问专用目录。

若要从源专用文件系统打开文件,请使用 navigator.storage 基于 Promise 的 API:

// Get the origin-private directory handle.
const root = await navigator.storage.getDirectory();
// Get the handle for a file in the directory.
const fileHandle = await root.getFileHandle("my-file.txt");

存储配额

在 Microsoft Edge 中,本地和会话存储分别限制为大约 5MB。

其他类型的数据存储(如 IndexedDB、缓存 API 或源专用文件系统访问 API)最多可以使用设备上总磁盘空间的 60%。 例如,如果运行应用的设备具有 64GB 磁盘,Microsoft Edge 允许应用存储最多约 38GB 的数据。

请注意,设备上实际可用的可用空间可能小于 60% 的存储配额。 例如,如果运行应用的设备具有 64GB 磁盘,但操作系统和其他文件已使用 50GB,则即使存储配额仍为 38GB,你的应用也只能存储 14GB 的数据。

可以使用 navigator.storage.estimate() 来询问存储管理器 API 应用的源存储配额是什么,以及已使用多少。 若要了解详细信息,请参阅 MDN 上的 StorageManager.estimate ()

尝试存储超过可用或允许的数据会导致 JavaScript 错误。 代码应使用 try...catch 语句捕获此错误。 下面的代码片段演示了在使用 Web 存储存储数据时如何捕获超出配额的错误:

try {
  localStorage.setItem('foo', 'bar');
} catch (e) {
  // Code that handles the lack of storage space.
}

数据逐出

当用户的设备开始不足可用磁盘空间(也称为 存储压力)时,Microsoft Edge 将开始逐出非持久性数据。

这意味着,应用使用缓存 API、IndexedDB、源专用文件系统访问 API 或 Web 存储存储的数据可能会被逐出。

默认情况下,应用存储的数据不被视为永久性数据,在存在存储压力时可能会被逐出。 如果应用存储关键数据,请使用 navigator.storage.persist() 函数使应用的存储持久化。 持久存储只能由用户清除。 若要了解详细信息,请参阅 MDN 上的 StorageManager.persist ()