有些网站可能需要与 Service Worker 通信, 获知结果。下面是一些示例:
- 页面向 Service Worker 发送一个网址列表, 预提取的内容,以便在用户点击 链接了缓存中已经可用的文档或页面子资源,使得后续 大大加快导航速度
- 页面要求 Service Worker 检索并缓存一组热门文章, 以供离线使用
将这些类型的非关键任务委托给 Service Worker 的好处是, 主线程,以便更好地处理更紧迫的任务,例如响应用户交互。
![一个页面请求将资源缓存到 Service Worker 的示意图。](https://web.developers.google.cn/static/articles/imperative-caching-guide/image/diagram-a-page-requestin-f19107d4e4aec.png?authuser=9&hl=fi)
在本指南中,我们将探讨如何在网页和各个环节之间实现单向通信技术。 标准浏览器 API 和 Workbox 库来运行 Service Worker。我们将这些类型的 命令式缓存使用场景。
制作案例
1-800-Flowers.com 通过以下方式通过 Service Worker 实现命令式缓存(预提取):
postMessage()
,用于预取
热门商品,以加快后续导航到商品详情页面的速度。
![1-800 Flowers 的徽标。](https://web.developers.google.cn/static/articles/imperative-caching-guide/image/logo-1-800-flowers-94f008b879df2.png?authuser=9&hl=fi)
它们使用混合方法来决定要预提取哪些项:
- 在页面加载时,它们要求 Servicer Worker 检索前 9 项的 JSON 数据,并且 将生成的响应对象添加到缓存中。
- 对于其余项,它们会监听
mouseover
事件触发 当用户将光标移到某项内容上方时,他们可以“按需”触发对该资源的提取。
它们使用 Cache API 存储 JSON 响应:
<ph type="x-smartling-placeholder">![1-800 Flowers 的徽标。](https://web.developers.google.cn/static/articles/imperative-caching-guide/image/logo-1-800-flowers-ee1b000c680c6.png?authuser=9&hl=fi)
当用户点击某个商品时,可以从缓存中获取与其关联的 JSON 数据, 也无需连接到网络,从而加快导航速度。
使用 Workbox
Workbox 可让您轻松地将邮件发送至
一个 Service Worker,通过 workbox-window
软件包提供,
旨在在窗口上下文中运行的应用。它们是对其他 Workbox 软件包的补充
在 Service Worker 中运行的应用
要与 Service Worker 通信页面,请先获取指向 注册的 Service Worker:
const wb = new Workbox('/sw.js');
wb.register();
然后,您可以直接以声明方式发送消息,免去获取 注册、检查激活情况或考虑底层通信 API:
wb.messageSW({"type": "PREFETCH", "payload": {"urls": ["/data1.json", "data2.json"]}}); });
Service Worker 实现 message
处理程序,
收听这些消息。它可以选择性地返回响应
不需要:
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'PREFETCH') {
// do something
}
});
使用浏览器 API
如果 Workbox 库无法满足您的需求,您可以通过以下方式实现窗口到服务 通过浏览器 API 实现工作器通信。
postMessage API 可用于建立从页面到 Service Worker 的单向通信机制。
网页调用
postMessage()
(位于
Service Worker 接口:
navigator.serviceWorker.controller.postMessage({
type: 'MSG_ID',
payload: 'some data to perform the task',
});
Service Worker 实现 message
处理程序,
收听这些消息。
self.addEventListener('message', (event) => {
if (event.data && event.data.type === MSG_ID) {
// do something
}
});
{type : 'MSG_ID'}
属性并非绝对必要,但可让网页
向 Service Worker 发送不同类型的指令(即“预提取”与“清除”
storage')。Service Worker 可以根据此标志分支到不同的执行路径。
如果操作成功,用户将能够从中受益,但是如果没有,则不会影响主要的用户流。例如,当 1-800-Flowers.com 尝试预缓存时,页面无需知道 Service Worker 是否成功。如果支持,用户将享受到更快速的导航体验。如果没有,页面仍然需要导航到新页面。这需要多一点时间。
简单的预提取示例
命令式缓存的最常见应用之一是预提取,这意味着 从而加快导航速度。
您可以通过以下不同方式在网站中实现预提取:
- 在网页中使用链接预提取代码:资源会保存在
浏览器缓存五分钟,之后系统会对资源应用正常的
Cache-Control
规则 。 - 通过服务中的运行时缓存策略对之前的技术进行补充 worker 来延长预提取的生命周期 超过此限制的资源。
对于相对简单的预提取场景,例如预提取文档或特定资源(JS、 CSS 等),那么这些技术是最佳方法。
如果需要其他逻辑,例如在 为了提取其内部网址,更合适的做法是将此任务完全委托给 Service Worker。
将这些类型的操作委托给 Service Worker 具有以下优势:
- 分流了抓取和抓取后处理的繁重工作(我们将介绍 发送到辅助线程。这样就能释放主线程来处理更重要的任务 例如响应用户互动
- 允许多个客户端(如标签页)重复使用一项常用功能,甚至可以调用 而无需阻塞主线程。
预提取商品详情页面
首次使用 postMessage()
的日期:
Service Worker 接口,并传递要缓存的网址数组:
navigator.serviceWorker.controller.postMessage({
type: 'PREFETCH',
payload: {
urls: [
'www.exmaple.com/apis/data_1.json',
'www.exmaple.com/apis/data_2.json',
],
},
});
在 Service Worker 中,实现 message
处理程序
拦截和处理任何活动标签页发送的消息:
addEventListener('message', (event) => {
let data = event.data;
if (data && data.type === 'PREFETCH') {
let urls = data.payload.urls;
for (let i in urls) {
fetchAsync(urls[i]);
}
}
});
在之前的代码中,我们引入了一个名为 fetchAsync()
的小型辅助函数,用于迭代
网址数组,并针对每个网址发出提取请求:
async function fetchAsync(url) {
// await response of fetch call
let prefetched = await fetch(url);
// (optionally) cache resources in the service worker storage
}
获取响应后,您可以依赖资源的缓存标头。在很多情况下
但和商品详情页面一样,资源不会被缓存(这意味着它们
no-cache
的 Cache-control
标头)。在此类情况下,您可以替换此行为,
将提取的资源存储在 Service Worker 缓存中。这样做还有一个好处,那就是
文件。
不局限于 JSON 数据
从服务器端点提取 JSON 数据后,这些数据通常会包含 例如,与这一第一级相关联的图片或其他端点数据。 数据。
假设在我们的示例中,返回的 JSON 数据是某个杂货店购物网站的信息:
{
"productName": "banana",
"productPic": "https://cdn.example.com/product_images/banana.jpeg",
"unitPrice": "1.99"
}
修改 fetchAsync()
代码,以遍历商品列表并缓存主打图片
分别为
async function fetchAsync(url, postProcess) {
// await response of fetch call
let prefetched = await fetch(url);
//(optionally) cache resource in the service worker cache
// carry out the post fetch process if supplied
if (postProcess) {
await postProcess(prefetched);
}
}
async function postProcess(prefetched) {
let productJson = await prefetched.json();
if (productJson && productJson.product_pic) {
fetchAsync(productJson.product_pic);
}
}
对于 404 等情况,您可以在此代码周围添加一些异常处理。 但使用 Service Worker 进行预提取的优点是它可能会失败且不会太多 对网页和主线程造成的不良影响您在本单元的 对预提取内容进行后处理,使其更加灵活,并且与 处理。讨论内容海阔天空。
总结
在本文中,我们介绍了页面与服务之间的单向通信的常见用例。 工作器:命令式缓存。这些示例仅用于演示 使用这种模式,同样的方法也可应用于其他用例,例如, 按需缓存热门文章以供离线阅读、添加书签等。
如需了解页面和 Service Worker 的更多通信模式,请参阅: