Skip to content

Service Worker

统计信息:字数 5479 阅读11分钟

简介

Service worker 是后台运行的一个 JavaScript 脚本。它由前台页面的 JavaScript 脚本新建,运行在独立的线程。它是异步的,不会造成页面的堵塞,可以发出通知(push notification),但拿不到 DOM。它要求页面必须使用 HTTPS 协议。

Service Worker 一共有 6 种状态。

  • parsed
  • installing
  • installed
  • activating
  • activated
  • redundant

登记 Service Worker。

/* In main.js */
if ("serviceWorker" in navigator) {
  navigator.serviceWorker
    .register("./sw.js")
    .then(function (registration) {
      console.log("Service Worker Registered", registration);
    })
    .catch(function (err) {
      console.log("Service Worker Failed to Register", err);
    });
}

一旦登记成功,Service Worker 的状态就变成了parsed

然后,浏览器尝试安装 Service Worker 脚本,这时它的状态就变成了installing

/* In main.js */
navigator.serviceWorker.register("./sw.js").then(function (registration) {
  if (registration.installing) {
    // Service Worker is Installing
  }
});

一旦进入安装阶段,Service Worker 脚本会接收到一个install事件。这个事件的回调函数里面,可以缓存静态文件。

/* In sw.js */
var cacheName = "your-first-service-worker";
var urlsToCache = [
  "/",
  "css/tachyons.min.css",
  "img/andre-benz-248755.jpg",
  "img/andre-benz-250740.jpg",
  "img/andre-benz-256762.jpg",
  "img/redd-angelo-230297.jpg",
];

self.addEventListener("install", function (event) {
  // Perform install steps
  event.waitUntil(
    caches.open(cacheName).then(function (cache) {
      console.log("Opened cache");
      return cache.addAll(urlsToCache);
    })
  );
});

上面代码中,事件对象有一个event.waitUntil()方法,只有这个方法内部的 Promise 变成resolved以后,installing事件才会成功。如果 Promise 变成rejectedinstalling事件就会失败,Service Worker 变成redundant状态。

/* In sw.js */
self.addEventListener('install', function(event) {
  event.waitUntil(
    Promise.reject(); // Failure
  );
});

如果安装成功,Service Worker 就变成installed状态,或者也叫waiting状态。这时,Service Worker 是有效的,但还没有被激活。它还不受当前文档控制,而是等待接受控制的状态。

主脚本之中可以检查,是否处于waiting状态。

/* In main.js */
navigator.serviceWorker.register("./sw.js").then(function (registration) {
  if (registration.waiting) {
    // Service Worker is Waiting
  }
});

这时可以通知用户升级版本,或者为他们自动更新。

以下几种情况,Service Worker 会进入activating状态。

  • 当前没有活跃的 Worker
  • Service Worker 脚本里面,调用了self.skipWaiting()方法
  • 用户离开当前页面,因此释放了前一个活跃的 worker
  • 经过一段时间,前一个活跃的 worker 已经释放

进入activating状态时,Service Worker 脚本会接收到active事件,通常在这个事件的回调函数里面清除旧缓存。

/* In sw.js */
self.addEventListener("activate", function (event) {
  event.waitUntil(
    // Get all the cache names
    caches.keys().then(function (cacheNames) {
      return Promise.all(
        // Get all the items that are stored under a different cache name than the current one
        cacheNames
          .filter(function (cacheName) {
            return cacheName != currentCacheName;
          })
          .map(function (cacheName) {
            // Delete the items
            return caches.delete(cacheName);
          })
      ); // end Promise.all()
    }) // end caches.keys()
  ); // end event.waitUntil()
});

上面代码中,也有一个event.waitUntil()方法,只有它里面的 Promise 变成resolved,激活才能成功。否则,就会激活失败,Service Worker 变成redundant

如果激活成功,Service Worker 就变成active状态,这时 Service Worker 会完全控制文档。我们可以这样检查是否进入这个状态。

/* In main.js */
navigator.serviceWorker.register("./sw.js").then(function (registration) {
  if (registration.active) {
    // Service Worker is Active
  }
});

一旦 Service Worker 激活,它就能处理 fetch 和 message 事件。

/* In sw.js */

self.addEventListener("fetch", function (event) {
  // Do stuff with fetch events
});

self.addEventListener("message", function (event) {
  // Do stuff with postMessages received from document
});

由于以下原因,Service Worker 会变成redundant状态。

  • installing 事件失败
  • activating 事件失败
  • 一个新的 Service Worker 取代了当前活跃的 Service worker

用户发出请求时,会触发fetch事件。

// Fetch the contents and reply with cache
self.addEventListener("fetch", function (event) {
  event.respondWith(
    caches.match(event.request).then(function (response) {
      // Cache hit - return response
      if (response) {
        return response;
      }
      return fetch(event.request);
    })
  );
});

参考链接


Last update: November 9, 2024