本文档中尚未着重介绍的一个 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 中,您可以 import
来自 workbox-window
的 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
事件,然后再注册 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 有更新时通知用户。该方案依赖于名为 messageSkipWaiting
的 self.skipWaiting
特殊辅助方法,该方法会发送 type
值为 SKIP_WAITING
的消息。