使用 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 中,您可以 import 来自 workbox-windowWorkbox 类:

<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 事件,然后再注册 Service Worker。这在涉及预缓存的情况下是可取的,从而可以避免可能会延迟页面启动的带宽争用。

window 和 Service Worker 范围之间通信

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 有更新时通知用户。该方案依赖于名为 messageSkipWaitingself.skipWaiting 特殊辅助方法,该方法会发送 type 值为 SKIP_WAITING 的消息。