Service Workers 是什么?

Service workers 本质上充当Web应用程序与浏览器之间的代理服务器,最初是为了适应越来越多的网页应用而产生。它可以通过拦截并修改访问和资源请求,缓存特定的网页资源,并可以将网页做到离线可用;同时 Service Workers 也能做到定期后台同步与信息推送。


Service Workers 对普通网站带来的好处

对于普通网站来说 Service Workers 可以细粒度地控制资源的缓存与加载,例如可以选择静态资源在网络可用时加载服务器中的资源,如果无法请求则使用本地已缓存的资源文件,也可以强制其使用已缓存文件以加快访问速度。或者是对于网络不具有强烈依赖性的网页应用,Service Workers 可以做到非常优秀的离线访问体验。


如何使用 Service Workers

在网页中使用 Service Workers 必须通过 https ,因为修改网络请求的能力暴露给中间人攻击会非常危险。

注册sw.js

<script>
  if ('serviceWorker' in navigator) {
      window.addEventListener('load', function () {
          navigator.serviceWorker.register('/sw.js').then(function (registration) {
              console.log('ServiceWorker registration successful with scope: ', registration.scope);
          }).catch(function (err) {
              console.log('ServiceWorker registration failed: ', err);
          });
      });
  }
</script>

在网站根目录新建sw.js,需要注意是sw.js所在的目录位置代表其影响的范围。
Service Workers 拥有原生API,不过支持的功能较少。这里使用sw-toolbox.js来实现缓存控制(sw-toolbox属于运行时缓存,同时GoogleChromeLabs也开发了sw-precache来控制预缓存)
我将sw-toolbox.js放置在/js/sw-toolbox.js,并在sw.js中引用。

importScripts("/js/sw-toolbox.js");

sw-toolbox 的详细API说明可以在这里找到https://googlechromelabs.github.io/sw-toolbox/api.html

它提供5种缓存策略

  1. toolbox.networkFirst
    先尝试获取服务器中的资源并更新本地缓存,如果未获取到,再使用本地已缓存的资源。
  2. toolbox.cacheFirst
    如果请求资源已缓存,则使用缓存资源。否则请求网络资源,并更新本地缓存。
  3. toolbox.fastest
    同时请求缓存和网络资源,先请求到的资源会先使用。这个策略总是会发起网络请求,如果当网络请求成功时,缓存将会被更新。
  4. toolbox.cacheOnly
    只从缓存中获取资源,无缓存则失败(配合预缓存使用)
  5. toolbox.networkOnly
    只使用网络请求,适合于动态内容。

sw.js中设定缓存规则

定义缓存版本、名称、以及最大缓存数量

importScripts("/js/sw-toolbox.js");
var cacheVersion = "-17104";
var staticImageCacheName = "image" + cacheVersion;
var staticAssetsCacheName = "assets" + cacheVersion;
var contentCacheName = "content" + cacheVersion;
var vendorCacheName = "vendor" + cacheVersion; 
var maxEntries = 100; /* 最大缓存数量 */
var maxAgeSeconds = 60*60*24; /* 最大缓存时间,单位(s) */

设定图床的缓存策略

self.toolbox.router.get("/(.*)", self.toolbox.networkFirst, {
    origin: /source\.isthnew\.com/,
    cache: {
        name: staticImageCacheName,
        maxEntries: maxEntries
      }
  });

设定网页内容缓存

self.toolbox.router.get("/(.*)", self.toolbox.networkFirst, {
    origin: /www\.isthnew\.com/,
    cache: {
       name: contentCacheName,
       maxEntries: maxEntries
}
  });

静态内容缓存(这里其实可以使用toolbox.fastest缓存规则)

self.toolbox.router.get("/css/(.*)", self.toolbox.networkFirst, {origin: /www\.isthnew\.com/,});
self.toolbox.router.get("/js/(.*)", self.toolbox.networkFirst, {origin: /www\.isthnew\.com/,});
self.toolbox.router.get("/fonts/(.*)", self.toolbox.networkFirst, {origin: /www\.isthnew\.com/,});
self.toolbox.router.get("/images/(.*)", self.toolbox.networkFirst, {origin: /www\.isthnew\.com/,});

我的博客使用了 Google Analytics 统计和 Disqus 评论系统,让他们调用的js都直接使用缓存

self.toolbox.router.get("/(.*)", self.toolbox.cacheFirst, {origin: /(www\.google-analytics\.com)/,
    cache: {
        name: vendorCacheName,
        maxEntries: maxEntries
    }
});
self.toolbox.router.get("/(.*)", self.toolbox.cacheFirst, {
    origin: /disquscdn\.com/,
    cache: {
        name: vendorCacheName,
        maxEntries: maxEntries
    }
});
self.toolbox.router.get("/(.*)", self.toolbox.cacheFirst, {
    origin: /isthnew\.disqus\.com/,
    cache: {
        name: vendorCacheName,
        maxEntries: maxEntries
    }
});

使sw.js在线时更新缓存策略

self.toolbox.router.get("/sw.js",self.toolbox.networkFirst),
self.addEventListener("install",

每次页面加载时强制更新 Service Worker

function(event) {return event.waitUntil(self.skipWaiting())
});
self.addEventListener("activate",
function(event) {return event.waitUntil(self.clients.claim())
})

调试

Chrome 等浏览器支持在本地使用 localhost 调试 Service Worker ,无需 https 。在 Chrome 的 Developer Tools – Application – Server Worker 即可看到当前 Service Worker 的状态,并可以手动更新、同步、注销。


浏览器支持

目前 Chrome Firefox Opera 已经全面支持,Edge Safari均作为需要手动打开的实验性特性。
http://caniuse.com/#feat=serviceworkers


参考文档:
[1] 本博客对 sw-toolbox 的实践 – neoFelhz
[2] sw-toolbox API
[3] Service Worker API