Service Worker实现离线缓存

上一篇说过的manifest离线缓存方法虽然被所有主流浏览器的最新版本所支持,但终究还是在web标准中被废弃了,firefox在控制台会提醒使用Service Worker缓存替代。

但其实Service Worker还远远没有被普遍支持,仅chrome、firefox和opera的新版本支持,Edge和safari的支持尚在开发中,而且Service Worker要求必须在https网站上使用。所以实际上,在相当长的时间内manifest缓存会一直被各大浏览器支持,不会被轻易删除。新标准的普及需要时间,旧标准的完全退出更需要时间,这个时间,可能长于大部分网站的生命周期……

但我还是先试了一下Service Worker,方法同样很简单,第一步先注册Service Worker,类似于manifest方法中在html元素内添加清单文件,只不过这里是在页面注册一个js文件,引入以下js代码即可。

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

注册的这个js文件所在的路径会影响Service Worker的作用域,它在哪个目录下,作用域就包含哪个目录。比如“/qifu/sw.js”就会导致作用域是/qifu/及其下属目录,而放在根目录的话,作用域就是全站。

下面是注册的Service Worker文件内容(/qifu/sw.js)。

var CACHE_NAME = 'qifu';
//缓存名称
var urlsToCache = [
  '/qifu/',
  'style.css',
  'script.js'
];
//要缓存的资源url
self.addEventListener('install', function(event) {
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then(function(cache) {
        console.log('Opened cache');
        return cache.addAll(urlsToCache);
      })
  );
});
//初始化安装,缓存上面列出的资源
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就拦截所有请求,有缓存就直接返回缓存内容。初始化只有第一次以及Service Worker文件有更新后才会触发,否则会被自动跳过。

Service Worker缓存之所以被推出用来替代manifest缓存,一个很重要的原因就是更灵活。比如即使初始化时没有缓存某些文件,也可以在后续用到时自动缓存(具体代码本文不再列出,只比上述代码多几行),而不像manifest一样必须逐一列出要缓存的内容,还不允许通配符CACHE。

由于目前浏览器对于Service Worker的支持尚不广泛,可以同时保留manifest缓存,如果支持的话,浏览器检测到两者并存会自动使用Service Worker。

类似于manifest缓存,每次打开浏览器,会使用缓存的数据,然后浏览器默默查看Service Worker文件有没有更新,只要有哪怕一个字节的变化,就会重新缓存,新内容同样是下次访问生效。(大部分网站可能都为js文件设置了HTTP缓存,目前浏览器在检查Service Worker文件更新时会遵守http header里面设置的缓存时间,但最长24小时,如果超过了这一数值,浏览器会重新下载Service Worker的js文件。)

如果更新后的Service Worker定义了新的缓存名称,通常希望删掉旧的缓存,可以用下面的代码:

self.addEventListener('activate', function(event) {
  var cacheWhitelist = ['not-delete-cache-a', 'not-delete-cache-b'];
//上面是不要删除的缓存名,其余全部删除
  event.waitUntil(
    caches.keys().then(function(cacheNames) {
      return Promise.all(
        cacheNames.map(function(cacheName) {
          if (cacheWhitelist.indexOf(cacheName) === -1) {
            return caches.delete(cacheName);
          }
        })
      );
    })
  );
});

好了,就这么简单。

随便看看

本文共有20条评论

  1. 大神,如果是跨域的资源呢?
    主站是www.example.com
    资源是 asset.example.com/imgs/example.css
    之类的路径,怎么安排缓存?

    1. 不是大神,纯业余,我自己没尝试过,但是根据网上的描述,引用的跨域资源也是受Service Worker缓存控制的。

你好,哪位? 填写