使用 workbox-window

本文档中尚未详细介绍的一个 Workbox 模块是 workbox-window,它是一组旨在在 window 中运行的模块。本单元的目标是:

  • 通过帮助开发者识别 Service Worker 生命周期的关键时刻,简化 Service Worker 注册和更新,从而更轻松地在这些时刻做出响应。
  • 为了防止开发者犯下常见错误,例如在错误的作用域中注册 Service Worker。
  • 简化 windowService Worker 范围之间的消息传递。

导入并使用 workbox-window

您从 workbox-window 最常使用的导出内容是 Workbox 类,您可以将其导入 Node,也可以从网页中的 CDN 导入。

创建本地软件包

如果您的工具链包含 webpackRollup 等捆绑器,您可以在本地捆绑 workbox-window

首先,将 workbox-window 安装为应用的正式版依赖项:

npm install workbox-window --save

然后,在应用 JavaScript 中,您可以从 workbox-window import Workbox 类:

<script type="module">
import {Workbox} from 'workbox-window';

if ('serviceWorker' in navigator) {
  const wb = new Workbox('/sw.js');

  wb.register();
}
</script>

虽然 workbox-window 非常小,但您可以使用动态 import 将其从网站的核心应用逻辑中拆分出来,这样可以缩减网页主 bundle 的大小:

<script type="module">
if ('serviceWorker' in navigator) {
  const {Workbox} = await import('workbox-window');

  const wb = new Workbox('/sw.js');
  wb.register();
}
</script>

使用 CDN

虽然不是推荐的方法,但使用 workbox-window 的更简单方法是从 CDN 导入:

<script type="module">
  import {Workbox} from 'https://storage.googleapis.com/workbox-cdn/releases/6.2.0/workbox-window.prod.mjs';

  if ('serviceWorker' in navigator) {
    const wb = new Workbox('/sw.js');

    wb.register();
  }
</script>

您会注意到,上述示例中的 <script> 元素使用了 type="module" 属性。如果您想在浏览器中使用静态 import 语句,而无需构建步骤,则必须执行此操作。支持 Service Worker 的所有主要浏览器也都支持 JavaScript 模块,因此可以将此代码提供给任何浏览器,因为旧版浏览器会忽略 type 属性值为 "module"<script> 元素。

注册 Service Worker

如需向 workbox-window 注册 Service Worker,请使用 Workbox 类的 register 方法,如下所示:

import {Workbox} from 'workbox-window';

const wb = new Workbox('/sw.js');
wb.register();

这似乎与使用 navigator.serviceWorker.register 自行注册 Service Worker 一样。不过,Workbox.register 会等到 window load 事件发生后再注册服务工件。在涉及预缓存的情况下,这样做是可取的,以避免可能延迟网页启动的带宽争用。

window 和服务工件作用域之间通信

Service Worker 有自己的作用域,与 window 分开,并且只能访问 window 中可用的一部分 API。不过,可以在 window 和 Service Worker 之间进行通信。workbox-window 允许使用 workbox-window 模块的 messageSW 方法更轻松地在两个作用域之间进行通信。

Workbox 使用特定格式的消息,该消息是一个具有以下属性的对象:

  • type 是用于标识消息的必需唯一字符串。格式应采用大写形式,并使用下划线将字词分隔开来(例如 CACHE_URLS)。
  • meta 是一个可选字符串,表示发送消息的 Workbox 软件包的名称,通常省略。
  • payload 是一个可选参数,表示您要发送的数据。它可以是任何数据类型。

以下示例展示了 messageSW 的运作方式,从 Service Worker 中的代码开始:

// sw.js
const SW_VERSION = '1.0.0';

self.addEventListener('message', (event) => {
  if (event.data.type === 'GET_VERSION') {
    event.ports[0].postMessage(SW_VERSION);
  }
});

然后在网页中添加以下代码:

const wb = new Workbox('/sw.js');
wb.register();

const swVersion = await wb.messageSW({type: 'GET_VERSION'});
console.log('Service Worker version:', swVersion);

在很多情况下,Service Worker 与 window 之间的通信可能会很有用,例如在有可用的 Service Worker 更新时通知用户。该方案依赖于一个名为 messageSkipWaiting 的特殊 self.skipWaiting 辅助方法,该方法会发送 type 值为 SKIP_WAITING 的消息。