Service Worker 生命周期

Jake Archibald
Jake Archibald

服务工件的生命周期是其最复杂的部分。如果您不知道它在尝试做什么以及有哪些好处,就会觉得它在与您对抗。不过,了解其运作方式后,您就可以将 Web 和原生模式的优势相结合,为用户提供无缝且不显眼的更新。

本文将深入探讨该功能,但每个部分开头的项目符号已涵盖您需要了解的大部分内容。

intent

生命周期的目的是:

  • 实现离线优先。
  • 允许新服务工件在不中断当前服务工件的情况下做好准备。
  • 确保在整个过程中,网页都在同一 Service Worker(或无 Service Worker)的控制下。
  • 确保一次只运行一个版本的网站。

最后一个非常重要。如果没有服务工件,用户可以将一个标签页加载到您的网站,然后稍后再打开另一个标签页。这可能会导致您的网站同时运行两个版本。有时这样做没问题,但如果您要处理存储空间,很容易就会出现两个标签页对共享存储空间的管理方式有截然不同的看法。这可能会导致错误,更糟糕的是,导致数据丢失。

第一个 Service Worker

简而言之:

  • install 事件是服务工件收到的第一个事件,并且只会发生一次。
  • 传递给 installEvent.waitUntil() 的 promise 会指示安装时长以及安装是否成功或失败。
  • 服务工件只有在成功完成安装并变为“活跃”状态后,才会收到 fetchpush 等事件。
  • 默认情况下,除非网页请求本身通过了 Service Worker,否则网页的提取操作不会通过 Service Worker。因此,您需要刷新页面才能看到服务工件的效果。
  • clients.claim() 可以替换此默认值,并控制非受控页面。

以以下 HTML 为例:

<!DOCTYPE html>
An image will appear here in 3 seconds:
<script>
  navigator.serviceWorker.register('/sw.js')
    .then(reg => console.log('SW registered!', reg))
    .catch(err => console.log('Boo!', err));

  setTimeout(() => {
    const img = new Image();
    img.src = '/dog.svg';
    document.body.appendChild(img);
  }, 3000);
</script>

它会注册一个服务工件,并在 3 秒后添加狗的图片。

下面是其服务工件 sw.js

self.addEventListener('install', event => {
  console.log('V1 installing…');

  // cache a cat SVG
  event.waitUntil(
    caches.open('static-v1').then(cache => cache.add('/cat.svg'))
  );
});

self.addEventListener('activate', event => {
  console.log('V1 now ready to handle fetches!');
});

self.addEventListener('fetch', event => {
  const url = new URL(event.request.url);

  // serve the cat SVG from the cache if the request is
  // same-origin and the path is '/dog.svg'
  if (url.origin == location.origin && url.pathname == '/dog.svg') {
    event.respondWith(caches.match('/cat.svg'));
  }
});

它会缓存一张猫的图片,并在有 /dog.svg 请求时提供该图片。不过,如果您运行上述示例,则会在首次加载页面时看到狗。点击刷新,您就会看到猫。

范围和控制

Service Worker 注册的默认范围相对于脚本网址为 ./。这意味着,如果您在 //example.com/foo/bar.js 注册服务工件,其默认作用域为 //example.com/foo/

我们将网页、工作器和共享工作器称为 clients。您的服务工件只能控制范围内的客户端。客户端被“控制”后,其提取操作会通过作用域内的服务工作器进行。您可以检测客户端是否通过 navigator.serviceWorker.controller 进行控制,该值将为 null 或服务工件实例。

下载、解析和执行

您在调用 .register() 时会下载您的第一个 Service Worker。如果您的脚本在初始执行期间下载失败、解析失败或抛出错误,注册 promise 将被拒绝,并且系统会舍弃该服务工件。

Chrome 的开发者工具会在控制台中以及“应用”标签页的“服务工件”部分显示错误: