使用服务辅助角色管理网络请求

服务辅助角色是一种特殊类型的 Web 辅助角色 ,能够使用 Fetch API 截获、修改和响应网络请求。 服务工作者可以访问 Cache API 和异步客户端数据存储(如 IndexedDB)来存储资源。

服务辅助角色可以通过在本地缓存资源来加快 PWA 的速度,还可以通过使 PWA 独立于网络来提高可靠性。

用户首次访问 PWA 时,将安装其服务辅助角色。 然后,服务辅助角色与应用并行运行,即使应用未运行,也可以继续执行工作。

服务辅助角色负责截获、修改和响应网络请求。 当应用尝试从服务器加载资源,或发送从服务器获取数据的请求时,可能会发出警报。 发生这种情况时,服务辅助角色可以决定让请求转到服务器,或者截获请求并从缓存返回响应。

显示服务辅助角色位于应用与网络和缓存存储之间的高级体系结构关系图

注册服务辅助角色

与其他 Web 辅助角色类似,服务辅助角色必须存在于单独的文件中。 注册服务辅助角色时引用此文件,如以下代码所示:

if ("serviceWorker" in navigator) {
    navigator.serviceWorker.register("/serviceworker.js");
}

运行 PWA 的 Web 浏览器可能会为服务工作者提供不同级别的支持。 此外,运行 PWA 的上下文可能不安全。 因此,最好在运行任何与服务辅助角色相关的代码之前测试对象是否存在 navigator.serviceWorker 。 在上述代码中,使用位于站点根目录的文件注册 serviceworker.js 服务辅助角色。

请确保将服务辅助角色文件放在要管理的最高级别目录中。 此类目录称为服务辅助角色 的范围 。 在前面的代码中,该文件存储在应用的根目录中,服务辅助角色管理应用域名下的所有页面。

如果服务辅助角色文件存储在目录中 js ,则服务辅助角色的范围将限制为目录 js 和任何子目录。 最佳做法是将服务辅助角色文件放在应用的根目录中,除非需要缩小服务辅助角色的范围。

截获请求

在服务辅助角色中使用的main事件是 fetch 事件。 每当应用运行的浏览器尝试访问服务辅助角色范围内的内容时,该 fetch 事件都会运行。

以下代码演示如何为 fetch 事件添加侦听器:

self.addEventListener("fetch", event => {
  console.log('WORKER: Fetching', event.request);
});

在处理程序中 fetch ,可以控制请求是否转到网络、从缓存中拉取等。 根据请求的资源类型、更新频率以及应用程序特有的其他业务逻辑,你采用的方法可能会有所不同。

下面是在处理程序中可以执行的操作的 fetch 几个示例:

  • 如果可用,请从缓存返回响应;否则,回退到通过网络请求资源。
  • 从网络提取资源、缓存副本并返回响应。
  • 允许用户指定保存数据的首选项。
  • 为某些图像请求提供占位符图像。
  • 直接在服务辅助角色中生成响应。

服务辅助角色生命周期

服务辅助角色的生命周期由多个步骤组成,每个步骤触发一个事件。 可以将侦听器添加到这些事件,以运行代码来执行操作。 以下列表显示了服务辅助角色的生命周期和相关事件的高级视图。

  1. 注册服务辅助角色。

  2. 浏览器下载 JavaScript 文件、安装服务辅助角色并触发 install 事件。 可以使用 install 事件预先缓存任何重要的长期文件 (,例如 CSS 文件、JavaScript 文件、徽标图像或从应用) 的脱机页面。

    self.addEventListener("install", event => {
        console.log("WORKER: install event in progress.");
    });
    
  3. 服务辅助角色已激活,这会触发 activate 事件。 使用此事件清理过时的缓存。

    self.addEventListener("activate", event => {
        console.log("WORKER: activate event in progress.");
    });
    
  4. 当刷新页面或用户转到网站上的新页面时,服务辅助角色已准备好运行。 如果要在不等待的情况下运行服务辅助角色,请在事件期间install使用 self.skipWaiting() 方法,如下所示:

    self.addEventListener("install", event => {
        self.skipWaiting();
        // …
    });
    
  5. 服务辅助角色现在正在运行,可以侦 fetch 听事件。

预缓存资源

当用户首次访问应用时,将安装应用的 Service Worker。 install使用服务辅助角色中的 事件来检测何时发生这种情况,并缓存应用所需的所有静态资源。 缓存应用的静态资源 ((如起始页所需的 HTML、CSS 和 JavaScript 代码) ),即使用户的设备处于脱机状态,应用也能运行。

若要缓存应用的资源,请使用全局 caches 对象和 cache.addAll 方法,如下所示:

// The name of the cache your app uses.
const CACHE_NAME = "my-app-cache";
// The list of static files your app needs to start.
const PRE_CACHED_RESOURCES = ["/", "styles.css", "app.js"];

// Listen to the `install` event.
self.addEventListener("install", event => {
  async function preCacheResources() {
    // Open the app's cache.
    const cache = await caches.open(CACHE_NAME);
    // Cache all static resources.
    cache.addAll(PRE_CACHED_RESOURCES);
  }

  event.waitUntil(preCacheResources());
});

请注意,在初始安装之后, install 事件不会再次运行。 若要更新服务辅助角色的代码,请参阅 更新服务辅助角色

现在, fetch 可以使用 事件从缓存返回静态资源,而不是从网络再次加载它们:

self.addEventListener("fetch", event => {
  async function returnCachedResource() {
    // Open the app's cache.
    const cache = await caches.open(CACHE_NAME);
    // Find the response that was pre-cached during the `install` event.
    const cachedResponse = await cache.match(event.request.url);

    if (cachedResponse) {
      // Return the resource.
      return cachedResponse;
    } else {
      // The resource wasn't found in the cache, so fetch it from the network.
      const fetchResponse = await fetch(event.request.url);
      // Put the response in cache.
      cache.put(event.request.url, fetchResponse.clone());
      // And return the response.
      return fetchResponse.
    }
  }

  event.respondWith(returnCachedResource());
});

为简洁起见,上述代码示例不处理从网络获取请求 URL 失败的情况。

使用自定义脱机页

当应用使用多个 HTML 页面时,常见的脱机方案是在用户设备脱机时将页面导航请求重定向到自定义错误页:

// The name of the cache your app uses.
const CACHE_NAME = "my-app-cache";
// The list of static files your app needs to start.
// Note the offline page in this list.
const PRE_CACHED_RESOURCES = ["/", "styles.css", "app.js", "/offline"];

// Listen to the `install` event.
self.addEventListener("install", event => {
  async function preCacheResources() {
    // Open the app's cache.
    const cache = await caches.open(CACHE_NAME);
    // Cache all static resources.
    cache.addAll(PRE_CACHED_RESOURCES);
  }

  event.waitUntil(preCacheResources());
});

self.addEventListener("fetch", event => {
  async function navigateOrDisplayOfflinePage() {
    try {
      // Try to load the page from the network.
      const networkResponse = await fetch(event.request);
      return networkResponse;
    } catch (error) {
      // The network call failed, the device is offline.
      const cache = await caches.open(CACHE_NAME);
      const cachedResponse = await cache.match("/offline");
      return cachedResponse;
    }
  }

  // Only call event.respondWith() if this is a navigation request
  // for an HTML page.
  if (event.request.mode === 'navigate') {
    event.respondWith(navigateOrDisplayOfflinePage());
  }
});

更新服务辅助角色

安装新的服务辅助角色版本

如果对服务辅助角色代码进行更改并将新的服务辅助角色文件部署到 Web 服务器,则用户的设备将逐渐开始使用新的服务辅助角色。

每次用户导航到应用的某个页面时,运行应用的浏览器都会检查服务器上是否有新版本的服务辅助角色。 浏览器通过比较现有服务辅助角色和新服务辅助角色之间的内容来检测新版本。 检测到更改后,将安装新的服务辅助角色 () 触发其 install 事件,然后新的服务辅助角色将等待现有服务辅助角色停止在设备上使用。

实际上,这意味着可以同时运行两个服务辅助角色,但只有一个会截获应用的网络请求。 当应用关闭时,现有的服务辅助角色将停止使用。 下次打开应用时,会激活新的服务辅助角色。 将 activate 触发该事件,新的服务辅助角色开始截获 fetch 事件。

安装新的服务辅助角色后, self.skipWaiting() 可以通过在 Service Worker 的 install 事件处理程序中使用 来强制激活它。

若要详细了解服务辅助角色的更新方式,请参阅更新 web.dev 上的 服务辅助角色

更新缓存的静态文件

预缓存静态资源(如 CSS 样式表文件)时(如 预缓存资源中所述),应用仅使用文件的缓存版本,不会尝试下载新版本。

若要确保用户获取应用使用的静态资源的最新更改,请使用缓存破坏命名约定并更新服务辅助角色代码。

缓存破坏 意味着每个静态文件根据其版本命名。 这可以通过各种方式实现,但通常涉及使用生成工具,该工具读取文件的内容并根据内容生成唯一 ID。 该 ID 可用于命名缓存的静态文件。

接下来,更新服务辅助角色代码以在 期间 install缓存新的静态资源:

// The name of the new cache your app uses.
const CACHE_NAME = "my-app-cache-v2";
// The list of static files your app needs to start.
const PRE_CACHED_RESOURCES = ["/", "styles-124656.css", "app-576391.js"];

// Listen to the `install` event.
self.addEventListener("install", event => {
  async function preCacheResources() {
    // Open the app's cache.
    const cache = await caches.open(CACHE_NAME);
    // Cache the new static resources.
    cache.addAll(PRE_CACHED_RESOURCES);
  }

  event.waitUntil(preCacheResources());
});

// Listen to the `activate` event to clear old caches.
self.addEventListener("activate", event => {
  async function deleteOldCaches() {
    // List all caches by their names.
    const names = await caches.keys();
    await Promise.all(names.map(name => {
      if (name !== CACHE_NAME) {
        // If a cache's name is the current name, delete it.
        return caches.delete(name);
      }
    }));
  }

  event.waitUntil(deleteOldCaches());
});

比较上述代码片段与预缓存资源中的 和 值。CACHE_NAMEPRE_CACHED_RESOURCES 安装此新的服务辅助角色后,将创建新的缓存,并下载并缓存新的静态资源。 激活服务辅助角色后,将删除旧缓存。 此时,用户将拥有新版本的应用。

对服务辅助角色进行更改有时可能比较复杂。 使用库(如 Workbox )来简化静态资源生成步骤和服务辅助角色代码。

测试 PWA 中的网络连接

了解网络连接何时可用会很有帮助,以便同步数据或通知用户网络状态已更改。

使用以下选项测试网络连接:

属性 navigator.onLine 是一个布尔值,可让你知道网络的当前状态。 如果值为 true,则用户处于联机状态;否则,用户处于脱机状态。

若要了解详细信息,请参阅 MDN 上的 navigator.onLine

联机和脱机事件

可以在网络连接发生更改时采取措施。 可以侦听并采取措施来响应网络事件。 事件在 、 documentdocument.body 元素上window可用,如下所示:

window.addEventListener("online",  function(){
    console.log("You are online!");
});
window.addEventListener("offline", function(){
    console.log("Oh no, you lost your network connection.");
});

若要了解详细信息,请参阅 MDN 上的 Navigator.onLine

其他功能

服务工作者main责任是在网络连接不稳定的情况下使应用更快、更可靠。 服务辅助角色主要使用 fetch 事件和 Cache API 执行此操作,但他们可以将其他 API 用于高级方案,例如:

  • 数据的后台同步。
  • 定期同步数据。
  • 大型后台文件下载。
  • 推送消息的处理和通知。

后台同步

使用后台同步 API 允许用户继续使用你的应用并执行操作,即使用户的设备处于脱机状态。

例如,电子邮件应用可以允许其用户随时撰写和发送邮件。 应用前端可以尝试立即发送消息,如果设备脱机,服务辅助角色可以捕获失败的请求,并使用后台同步 API 延迟任务,直到连接。

若要了解详细信息,请参阅 使用后台同步 API 将数据与服务器同步

周期后台同步

定期后台同步 API 允许 PWA 定期在后台检索新内容,以便用户以后再次打开应用时可以立即访问内容。

通过使用定期后台同步 API,PWA 无需在用户使用应用时下载新内容 (如新文章) 。 下载内容可能会减慢体验,因此应用可以在更方便的时间检索内容。

若要了解详细信息,请参阅 使用定期后台同步 API 定期获取新内容

大型后台文件下载

后台提取 API 允许 PWA 将大量数据下载完全委托给浏览器引擎。 这样,在下载过程中,应用和服务辅助角色就无需运行。

此 API 适用于允许用户下载大型文件 ((如音乐、电影或播客)) 脱机用例的应用。 下载将委托给浏览器引擎,该引擎知道如何处理间歇性连接,甚至完全丢失连接。

若要了解详细信息,请参阅 在应用或服务辅助角色未运行时使用后台提取 API 提取大型文件

推送消息

推送消息可以发送给用户,而无需他们当时使用应用。 即使应用未运行,服务辅助角色也可以侦听服务器发送的推送消息,并在操作系统的通知中心显示通知。

若要了解详细信息,请参阅 使用推送消息重新吸引用户

使用 DevTools 进行调试

使用 Microsoft Edge DevTools,可以查看服务辅助角色是否已正确注册,并查看服务辅助角色当前处于哪个生命周期状态。 此外,还可以在服务辅助角色中调试 JavaScript 代码。

若要了解详细信息,请参阅 调试服务辅助角色

另请参阅