Service Worker 基础知识

本文旨在帮你快速了解 service worker 的基本知识点,让你对 service worker 有一个大致的了解。为以后开发一个简单的 PWA 应用打下基础。

关于 service worker 的几个基本知识点

  • 它是一个可编程的网络代理,让你可以控制页面请求的处理方式。
  • 它是一个 JavaScript Worker,因此它无法直接操作 DOM。但可以通过 postMessage 接口与页面通信。同时,service worker 中的代码不会阻塞页面响应。
  • 它在闲置时被终止,在需要时被启动。并不是常驻内存。因此你不能在 onfetch 或是 onmessage 回调中依赖全局状态。
  • 被设计成完全异步。因此在 service worker 中无法使用同步 API (例如同步 XHR,localStorage等)。接口重度依赖于 promise。
  • 只能在 HTTPS 页面加载(唯一的例外:localhost/127.0.0.1,方便调试)。

Service worker 的作用域

一个 service worker 的默认作用域是这个 service worker 脚本所在的目录。例如 https://example.com/sw.js 脚本默认就是 https://example.com 下的所有页面。

你也可以在注册 service worker 时明确指定作用域:

navigator.serviceWorker.register('sw.js', {
    scope: './abc'
});

假设以上代码在 https://example.com 页面里执行,则意味着该 service worker 的作用域就是 https://example.com/abc 下的页面。

我们把页面、workers 以及 shared workers 统称为 clients。你的 service worker 只能控制其作用域范围内的 clients。

你可以通过检查 navigator.serviceWorker.controller 属性来判断某个 client 是否受控于 service worker 之下。

Service worker 的生命周期

Service worker 的生命周期与页面的生命周期是完全独立的。

1. 注册 service worker

register() 时传入的 service worker 脚本的路径决定了此 service worker 的作用域。

  // 检测浏览器是否支持 service worker API
  if ('serviceWorker' in navigator) {
    navigator.serviceWorker.register('/sw.js').then(function(registration) {
      console.log('ServiceWorker registration successful with scope: ', registration.scope);
    }, function(err) {
      console.log('ServiceWorker registration failed: ', err);
    });
  }

2. install 事件

在注册 service worker 之后,从 service worker 的视角来看。它收到的第一个事件就是 install 事件。在 install 事件回调函数中,你可以:

  1. 打开一组缓存
  2. 缓存所需文件
  3. 检查所有需要的文件是否都已被缓存
var CACHE_NAME = 'my-site-cache-v1';
var urlsToCache = [
  '/',
  '/styles/main.css',
  '/script/main.js'
];

self.addEventListener('install', function(event) {
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then(function(cache) {
        return cache.addAll(urlsToCache);
      })
  );
});

3. activate 事件

activate 事件回调中通常要做的工作就是缓存管理。此时可以安全的清理之前版本 service worker 创建的缓存内容。

4. fetch 事件

安装完成以后,当页面发起网络请求时,会触发 service worker 的 fetch 事件。在事件回调函数中你可以决定如何处理该请求。

例如,优先从缓存中加载:

self.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request)
      .then(function(response) {
        // 命中缓存,直接把缓存的内容返回给页面
        if (response) {
          return response;
        }
        
        // 否则,请求网络
        return fetch(event.request);
      }
    )
  );
});

更新 service worker

更新一个 service worker 的流程大致如下:

  1. 修改 service worker 的脚本文件。当用户再次访问页面时,浏览器会尝试重新下载脚本文件。并与之前的版本比对。一旦发现文件内容不一致,就会进入更新流程。
  2. 新的 service worker 会被启动并触发 install 事件。
  3. 此时页面的控制器权还在老版 service worker 手中,而新版 service worker 进入 waiting 状态。
  4. 当前页面被关闭,老版 service worker 被终止。(注意:刷新页面不足以触发新老 service worker 交接)
  5. 用户再次访问页面,新版 service worker 被启动。触发 activate 事件。

注:要想在新版 service worker 安装完成后立刻接管页面而不必等到下一次加载页面。可以调用 self.skipWaiting() 方法跳过等待状态。

以上就是本篇的全部内容。希望能够帮你对 service worker 建立一个大致的了解。在下一篇文章中我会编写一个完整的例子。敬请期待!

参考资料

如果你觉得这篇文章对你有帮助,可以微信扫描以下二维码打赏作者

comments powered by Disqus