使用Service worker让页面支持离线访问

Google最近推行的Progressive Web Apps是个很酷的东西,而且采用的技术也是w3c的标准,不像微信小程序那样自己搞了一套封闭的东西。其实里边相关的一项技术service worker早在去年就有了,目前只有高版本的Chrome和Firefox支持,这个东西就可以把网页做成支持离线访问。

首先要在入口页面注册service worker

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/serviceworker.js').then(function(registration) {
    console.log('ServiceWorker registration successful with scope: ', registration.scope);
  }).catch(function(err) {
    console.log('ServiceWorker registration failed: ', err);
  });
}

注意这个serviceworker.js必须放在根目录下。

然后就是编辑这个serviceworker.js文件,控制哪些文件需要在本地缓存:

// 缓存名称
var ONLINE_CACHE_NAME = '2016-12-30 16:35';
// 缓存页面
var onlineCacheUrl = [
  '/',
  '/foo/bar/xxx.js',
  '/foo/bar/xxx.css',
  '/foo/bar/xxx.html',
  '/foo/bar/xxx.png',
];

删除过期的缓存

this.addEventListener('activate', function(event) {
  var cacheWhitelist = [ONLINE_CACHE_NAME];

  event.waitUntil(
    caches.keys().then(function(keyList) {
      return Promise.all(keyList.map(function(key) {
        if (cacheWhitelist.indexOf(key) === -1) {
          console.log('delete ' + key);
          return caches.delete(key);
        }
      }));
    })
  );
});
self.addEventListener('install', function(event) {
  event.waitUntil(
    caches.open(ONLINE_CACHE_NAME)
    .then(function(cache) {
      console.log('Opened cache');
      return cache.addAll(onlineCacheUrl);
    })
  );
});

下面这个fetch事件是最重要的,这相当于一个本地的代理服务器,所有的请求都会经过这里,然后把缓存里存在的东西原样返回,缓存里没有的东西从服务器获取再返回。

self.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request)
    .then(function(response) {
        if (response) {
            return response;
        }
        return fetch(event.request);
    })
  );
});

最后还有一点,除了本地调试用的localhost以外,只有https的页面才能采用service worker。可以参考这个最简单的demo:

service-worker-demo

首次打开页面以后,把网络关掉再次打开,就不会出现Chrome的小恐龙了。

serviceworker0

打开Chrome的调试工具,在Application -> ServiceWorker一栏可以看到已经成功启用。

serviceworker1