Service Worker

用户希望应用在网速较慢或不稳定的情况下能启动,甚至在离线状态下也能启动。他们希望最近互动过的内容(例如媒体曲目或票券和行程)能够正常显示和使用。 当无法发出请求时,用户希望应用通知他们,而不是静默失败或崩溃。而且用户希望能快速完成。我们在这项研究中可以看到,“毫秒提升,百万收益”,即使加载时间缩短 0.1 秒,转化率也可以提升多达 10%。总而言之:用户期望 PWA 非常可靠,这就是我们拥有 Service Worker 的原因。

Hello Service Worker

Service Worker 作为中间件代理,在 PWA 与服务器(包括您自己的服务器和跨网域服务器)之间在设备端运行。

当应用请求 Service Worker 的作用域涵盖的资源时(包括用户离线时),Service Worker 会拦截该请求,作为网络代理。然后,它可以决定是应该通过 Cache Storage API 从缓存提供资源,还是像在没有 Service Worker 一样从网络提供资源,或者通过本地算法创建资源。这样,您就可以提供与平台应用提供类似的体验,甚至可以完全离线工作。

注册 Service Worker

必须先为您的 PWA 注册 Service Worker,才能获取您的页面控制权。这意味着,当用户第一次访问您的 PWA 时,网络请求会直接发送到您的服务器,因为 Service Worker 尚不能控制您的网页。

检查浏览器是否支持 Service Worker API 后,您的 PWA 可以注册 Service Worker。加载后,Service Worker 会在 PWA 与网络之间建立商店,拦截请求并提供相应的响应。

if ('serviceWorker' in navigator) {
   navigator.serviceWorker.register("/serviceworker.js");
}

验证 Service Worker 是否已注册

要验证 Service Worker 是否已注册,请在您常用的浏览器中使用开发者工具。

在基于 Firefox 和 Chromium 的浏览器(Microsoft Edge、Google Chrome 或 Samsung Internet)中:

  1. 打开开发者工具,然后点击应用标签页。
  2. 在左侧窗格中,选择 Service Workers
  3. 检查 Service Worker 的脚本网址是否显示为“Activated”(已激活)状态。(您将在本章的“生命周期”部分中了解此状态的含义。)在 Firefox 中,状态可能是“正在运行”或“已停止”。

在 Safari 中:

  1. 点击 Develop 菜单,然后点击 Service Workers 子菜单。
  2. 检查子菜单中是否出现了包含当前来源的条目。它会在 Service Worker 的上下文上打开检查器。
Chrome、Firefox 和 Safari 上的 Service Worker 开发者工具。
Chrome、Firefox 和 Safari 上的 Service Worker 开发者工具。

范围

Service Worker 所在的文件夹决定了其作用域。位于 example.com/my-pwa/sw.js 的 Service Worker 可以控制 my-pwa 路径或路径下的任何导航,例如 example.com/my-pwa/demos/。Service Worker 只能控制其作用域内的项(页面、Worker,统称为“客户端”)。范围适用于浏览器标签页和 PWA 窗口。

每个范围仅允许 1 个 Service Worker。当处于活跃状态且运行时,通常只有一个实例可用,无论内存中有多少客户端(例如 PWA 窗口或浏览器标签页)。

Safari 具有更复杂的范围管理(称为分区),如果您使用的是跨网域 iframe,则会影响范围的工作方式。如需详细了解 WebKit 的实现,请阅读 WebKit 的博文

生命周期

Service Worker 的生命周期决定了其安装方式,这与 PWA 安装是分开的。Service Worker 生命周期从注册 Service Worker 开始。然后,浏览器尝试下载并解析 Service Worker 文件。如果解析成功,则会触发其 install 事件。install 事件仅触发一次。

Service Worker 会静默安装,无需用户许可,即使用户未安装 PWA 也是如此。Service Worker API 甚至在不支持 PWA 安装的平台(例如桌面设备上的 Safari 和 Firefox)上使用。

安装后,Service Worker 尚不能控制其客户端,包括您的 PWA。您需要先启用它。当 Service Worker 准备好控制其客户端时,将会触发 activate 事件。但这并不意味着注册 Service Worker 的网页将受到管理。默认情况下,由于重新加载页面或重新打开 PWA,Service Worker 在您下次导航到该页面时才会获得控制权。

您可以使用 self 对象监听 Service Worker 的全局范围内的事件。

serviceworker.js

// This code executes in its own worker or thread
self.addEventListener("install", event => {
   console.log("Service worker installed");
});
self.addEventListener("activate", event => {
   console.log("Service worker activated");
});

更新 Service Worker

当浏览器检测到当前控制客户端的 Service Worker 且同一文件的新版本(来自您的服务器)有字节不同时,Service Worker 会进行更新。

安装成功后,新的 Service Worker 将等待激活,直到现有(旧)Service Worker 不再控制任何客户端。此状态称为“waiting”,这是浏览器确保每次只运行一个 Service Worker 版本的方式。 刷新页面或重新打开 PWA 不会使新 Service Worker 取得控制权。用户需要使用当前 Service Worker 关闭或离开所有标签页和窗口,然后返回。只有这样,新的 Service Worker 才能取得控制权。 如需了解详情,请参阅这篇 Service Worker 生命周期文章

Service Worker 有效期

安装和注册 Service Worker 后,即可管理其作用域内的所有网络请求。它在自己的线程上运行,激活和终止由浏览器控制。这样一来,即使在打开 PWA 之前或之后,它也会正常运行。虽然 Service Worker 在自己的线程上运行,但无法保证内存中的状态在 Service Worker 的运行之间会持久保留,因此,请确保在每次运行时重复使用的内容在 IndexedDB 或其他持久性存储空间中都可用。

如果 Service Worker 尚未运行,则每当收到其作用域内的网络请求或者收到触发事件(如定期后台同步或推送消息)时,都将启动 Service Worker。

Service Worker 并非无限期存在。虽然确切时间因浏览器而异,但如果 Service Worker 已空闲几秒钟或处于忙碌状态,则会终止。如果 Service Worker 被终止,并且发生了会启动它的事件,则它将重启。

功能

有了已注册且处于活跃状态的 Service Worker,您的线程的执行生命周期将与 PWA 上的主线程完全不同。不过,在默认情况下,Service Worker 文件本身没有任何行为。它不会缓存或提供任何资源,因为这必须由您的代码完成。以下章节将为您介绍具体方法。

Service Worker 的功能不仅限于代理或处理 HTTP 请求;在其基础之上还提供了其他功能,例如后台代码执行、网络推送通知和处理付款。我们将在“功能”一章中讨论这些新增内容。

资源