本文档中尚未详细介绍的一个 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
的消息。