本文档中尚未详细介绍的一个 Workbox 模块是 workbox-window,它是一组旨在在 window 中运行的模块。本单元的目标是:
- 通过帮助开发者识别 Service Worker 生命周期的关键时刻,简化 Service Worker 注册和更新,从而更轻松地在这些时刻做出响应。
- 为了防止开发者犯下常见错误,例如在错误的作用域中注册 Service Worker。
- 简化
window与 Service Worker 范围之间的消息传递。
导入并使用 workbox-window
您从 workbox-window 最常使用的导出内容是 Workbox 类,您可以将其导入 Node,也可以从网页中的 CDN 导入。
创建本地软件包
如果您的工具链包含 webpack 或 Rollup 等捆绑器,您可以在本地捆绑 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 的消息。