前端缓存之Service Worker

什么是Service Worker

Service worker是一个注册在指定源和路径下的事件驱动worker。它采用JavaScript控制关联的页面或者网站,拦截并修改访问和资源请求,细粒度地缓存资源。Service Worker 可以使你的应用先访问本地缓存资源,包括js、css、png、json等多种静态资源。

Service Worker的特点

  1. 独立于主JavaScript线程(这就意味着它的运行丝毫不会影响我们主进程的加载性能)
  2. 设计完全异步,大量使用Promise(因为通常Service Worker通常会等待响应后继续,Promise再合适不过了)
  3. 不能访问DOM,不能使用XHR和localStorage
  4. Service Worker只能由HTTPS承载(出于安全考虑)

Service Worker用法

注册

使用 ServiceWorkerContainer.register() 方法注册service worker。

// sw.js
if ('serviceWorker' in navigator) {
    navigator.serviceWorker.register('/loading-page-sw.js', {scope: '/'}).then(function(reg) {
	// reg可以查看当前sw的状态和作用域等 
    }).catch(function(error) {
        // registration failed
        console.log('Registration failed with ' + error);
    });
}

缓存处理

  • caches.open(SW_VERSION):打开缓存, SW_VERSION: 版本
  • cache.addAll(CACHE_FILE_LIST):添加缓存, CACHE_FILE_LIST:文件路径列表
  • caches.keys():获取本地存储的版本集合
  • caches.delete(key): 删除某个版本的缓存信息
  • cache.put(event.request, responseClone):手动添加缓存,参数为request,response

下面附完整代码

// loading-page-sw.js
const SW_VERSION = 'V1';
const CACHE_FILE_TYPE = [ 'js','css', 'html','jpg','json','png''mp3','wav','mp4','ttf'];
//需要确认缓存的文件
const CACHE_FILE_LIST = [];
// 需要忽悠的文件列表
const IGNORE_FILE_LIST = [
  '/test/index.js',
];
/**
 * 是否是对应的文件类型
 * @param {*} url 
 */
function isAcceptFile(url) {
  var r = new RegExp("\\.(" + CACHE_FILE_TYPE.join('|') + ")$");
  return r.test(url);
}
/**
 * 检查文件名
 */
function checkIgnoreFileName(url) {
  var r = new RegExp("(" + IGNORE_FILE_LIST.join('|') + ")$");
  return r.test(url);
}
self.addEventListener('install', function(event) {
    event.waitUntil(
      caches.open(SW_VERSION).then(function(cache) {
        return cache.addAll(CACHE_FILE_LIST);
      })
    );
  });
self.addEventListener('activate', function(event) {
    var cacheWhitelist = [SW_VERSION];
    event.waitUntil(
      caches.keys().then(function(keyList) {
        return Promise.all(keyList.map(function(key) {
          if (cacheWhitelist.indexOf(key) === -1) {
            return caches.delete(key);
          }
        }));
      })
    );
  });
// 监听浏览器的所有fetch请求,对已缓存的资源使用本地缓存回复  
self.addEventListener('fetch', function(event) {
    const {method, url} = event.request;
    event.respondWith(
      caches.match(event.request).then(function(response) {
          if (response !== undefined) {
              return response;
          } else {
              return fetch(event.request).then(function (response) {
                  let responseClone = response.clone();
                  if (method === 'POST') {
                    return response
                  }
                  if (!isAcceptFile(url)) {
                    return response
                  }
                  if (checkIgnoreFileName(url)) {
                    return response
                  }
                  caches.open(SW_VERSION).then(function (cache) {
                    cache.put(event.request, responseClone);
                  });
                  return response;
              }).catch(function (error) {
                 return Promise.reject(error);
              });
          }
      })
    );
  });

service worker调试检查

查看service worker进程状态

service worker实际上提供的是本地缓存服务,所以和我们平时查看localStorage差不多,打开谷歌浏览器调试中心,在Application栏下,就能看到Service Woerkers,

可通过右侧Unregister来注销service worker进程。

查看本地存储信息

service worker会根据我们的版本在本地存储相应版本的文件,如图:

V1: 是我们自定义的版本号

缓存的文件已列表的形式被列出,信息有名称、返回类型、上下文长度、缓存时间等。

HTTP缓存

当我们的页面发起资源请求时,浏览器会通过缓存等手段来尽快响应,避免不必要的http消耗,所以我们经常见到:Memory Cache、Disk Cache、Push Cache,现在又多了一种ServiceWorker。我们来简单对比如下:

ServiceWorker

当我们启用ServiceWorker时,浏览器就会显示我们的资源来自ServiceWorker。Service Worker的缓存有别与其他缓存机制,我们可以自由控制缓存、文件匹配、读取缓存,并且是持续性的。当ServiceWorker没有命中时才会去重新请求数据。

Memory Cache

Memory Cache为内存中的缓存,读取内存中的资源是非常快的。 是浏览器为了加快读取缓存速度而进行的自身的优化行为,不受开发者控制,也不受 HTTP 协议头的约束。内存读取虽然快且高效,但它是短暂的,当浏览器或者tab页面关闭,内存就会被释放了。而且内存占用过多会导致浏览器占用计算机过大内存。

Disk Cache

Disk Cache 将资源存储在硬盘中,读取速度次于Memory Cache。

优点:可长期存储,可定义时效时间、容量大;缺点:读取速度慢。

根据http header 请求头触发的缓存策略来做缓存,包含强缓存和协商缓存

Push Cache

当以上三种缓存都没有命中时才会使用Push Cache。它只在会话(Session)中存在,一旦会话结束就被释放,并且缓存时间也很短暂。这种缓存一般用不到。

我们通过控制台network,最直观的查看资源用的缓存方式。如图所示:

Service Worker注销和删除

我们对Service Worker已经做了用法说明,并和其他缓存做了对比;最后我们来说说如何注销Service Worker和删除本地缓存吧!

注销

通过调用unregister()函数来注销,代码如下:

if ('serviceWorker' in navigator) {
    navigator.serviceWorker.getRegistrations()
    .then(function(registrations){
        registrations.forEach(function(registration) {
            registration.unregister();
        })
    })
}

删除

通过调用caches.delete()删除对,代码如下:

if (window.caches && caches.keys) {
    caches.keys().then(function(keys) {
        keys.forEach(function(key) {
            caches.delete(key)
        })
    })
}

写在最后

由于Service Worker是单独运行环境,独立于主JavaScript进程的,导致前端获取的navigator.userAgent和后台获取的userAgent不一样,若在项目中需要使用Service worker时需观察自身业务是否有影响,多做测试观察。

加载中...
加载中...