性能优化小册 - 可编程式缓存:Service Workers

Service Workers 不只能够应用于 PWA ,PC 端也能够利用其强大的功能实现一些有趣的优化,网络中有不少关于 Service Workers 介绍的比较好的文章,基于小册宗旨,本文并非一篇 Service Workers 的详细教程。

一些注意的点

Service worker 是一个注册在指定源和路径下的事件驱动 workercss

它运行在 worker 上下文中,不能访问 DOM,相对于驱动应用的主 JavaScript 线程,它运行在其余线程中,因此不会形成阻塞。html

它采用 JavaScript 控制关联的页面或者网站,拦截并修改访问和资源请求,细粒度地缓存资源。你能够彻底控制应用在特定情形(最多见的情形是网络不可用)下的表现。vue

它设计为彻底异步,同步 API(如 XHR 和 localStorage)不能在 Service worker 中使用。webpack

在已经支持 serivce worker 的浏览器的版本中,不少特性没有默认开启,web

了解浏览器对 serivce worker 的支持性。

若是你发现示例代码在当前版本的浏览器中怎么样都没法正常运行,你可能须要开启一下浏览器的相关配置chrome

另外,须要注意的是,出于安全缘由 Service Workers 要求必须在 HTTPS 下才能运行,为了便于本地开发,localhost 也被浏览器认为是安全源。bootstrap

注册 Service Worker

使用 ServiceWorkerContainer.register() 函数来注册站点的 service workerservice sorker 只是一个 JavaScript 脚本)。segmentfault

注意,这个文件的 url 是相对于 origin, 而不是相对于引用它的那个 JS 文件。
<!-- index.html -->
<script>
if ("serviceWorker" in navigator) {
  navigator.serviceWorker.register("/serviceworker.js")
    .then(function(reg) {
      if(reg.installing) {
        console.log('Service worker installing');
      } else if(reg.waiting) {
        console.log('Service worker installed');
      } else if(reg.active) {
        console.log('Service worker active');
      }
    })
    .catch(console.error);
}
</script>

配置缓存清单

Service Worker 注册以后,浏览器会尝试为你的页面或站点安装并激活它。 数组

install 事件会在注册完成以后触发,install 事件通常是被用来填充你的浏览器的缓存能力。浏览器

为了达成这个目的,咱们使用了 Service Worker 的新的标志性的存储 API — Cache,一个 service worker 上的全局对象,它使咱们能够存储网络响应发来的资源,而且根据它们的请求来生成 key

Cache 接口像 workers 同样,是暴露在 window 做用域下的,尽管它被定义在 service worker 的标准中, 可是它没必要必定要配合 service worker 使用。
// serviceworker.js
const cacheName = 'my-cache';
// self 表明 worker 线程自身,即子线程的全局对象
self.addEventListener('install', event => {
  // 用来存放缓存的静态资源和路由
  const filesToCache = [
    '/',
    '/static/css/reset.css',
    '/static/css/css-loader.css',
    '/static/css/create-version.css',
    '/static/css/bootstrap-grid.css',
    '/static/css/bootstrap.css',
    '/static/js/css-animations.js',
    '/static/js/vue.js',
  ];
  event.waitUntil(
    // caches 是 CacheStorage 的实例子:caches instanceof CacheStorage -> true
    // 使用 CacheStorage.open(cacheName) 打开一个 Cache 对象
    caches.open(cacheName)
    // 将字符串 URL 数组添加到缓存中
    .then(cache => cache.addAll(filesToCache))
    .catch(e => console.error(e))
  );
});

这里咱们新增了一个 install 事件监听器,接着在事件上接了一个 ExtendableEvent.waitUntil() 方法。

waitUntil() 用来确保 service worker 不会在 waitUntil() 里边的代码执行完毕以前安装完成。

若是 caches.open(cacheName)rejected,安装就会失败,这个 worker 不会作任何事情(例如:URL 拼写错误)。

注意:首次注册/激活 Service Worker 线程的页面须要再次加载才会受其控制(二次生效)。在成功安装完成并处于激活状态以前,Service Worker 线程不会收到 fetch (下文会提到)和 push 事件。

使用 fetch 拦截请求

如今已经将站点资源缓存了,还须要告诉 Service Worker 让它用这些缓存内容来作点什么,咱们能够借助 fetch API 进行一层拦截。

//fetch 事件处理程序,拦截请求并应用于全部缓存中的静态资产
self.addEventListener('fetch', e => {
    e.respondWith(
        caches.match(e.request)
        .then(response => response ? response : fetch(e.request))
    )
});

caches.match(event.request) 容许咱们对网络请求的资源和 cache 里可获取的资源进行匹配,查看是否缓存中有相应的资源。这个匹配经过 urlvary header 进行,就像正常的 http 请求同样。

查看 Fetch API documentation 了解更多有关 Request 和 Response 对象的更多信息。

当匹配到 catch 资源时,caches.match(event.request) 就会 resolve,在 then 回调中就能够直接返回 response

caches.match(e.request)
  .then(response => response)

若是没有匹配到资源,caches.match(event.request) 就会 reject,能够告诉浏览器直接使用 fetch 进行默认的网络请求。(意味着在网络可用的时候能够直接像服务器请求资源)

caches.match(e.request)
  .then(response => response ? response : fetch(e.request))

更新缓存

更新缓存清单,能够借助 activate 事件进行处理,本文案例代码并无对其进行实现。
只是使用 clients.claim() 对页面进行控制权获取,这样以后打开页面都会使用版本更新的缓存。

self.addEventListener('activate', e => self.clients.claim());

优化后的效果

Chrome 有一个 chrome://inspect/#service-workers 能够展现当前设备上激活和存储的 service worker。还有个 chrome://serviceworker-internals 能够展现更多细节来容许你开始/暂停/调试 worker 的进程。

经过 Chrome devToolsApplication Tab 咱们能够查看当前服务工做线程的运行状况。

serviceworker.js 完整代码:

const cacheName = 'my-cache';
// self.clients.claim() 取得页面控制权,这样以后打开页面都会使用版本更新的缓存
self.addEventListener('activate', e => self.clients.claim());

self.addEventListener('install', event => {
   // 用来存放缓存的静态资源和路由
  const filesToCache = [
    '/',
    '/static/css/reset.css',
    '/static/css/css-loader.css',
    '/static/css/create-version.css',
    '/static/css/bootstrap-grid.css',
    '/static/css/bootstrap.css',
    '/static/js/css-animations.js',
    '/static/js/vue.js',
  ];
  
  event.waitUntil(
    // caches 是 CacheStorage 的实例子:caches instanceof CacheStorage -> true
    // 使用 CacheStorage.open(cacheName) 打开一个 Cache 对象
    caches.open(cacheName)
    // 将 filesToCache (字符串 URL 数组)添加到缓存中
    .then(cache => cache.addAll(filesToCache))
    .catch(e => console.error(e))
  );
});
// fetch 进行拦截
self.addEventListener('fetch', e => {
    e.respondWith(
        caches.match(e.request)
        .then(response => {
         return  response ? response : fetch(e.request)
        })
    )
});

延伸阅读推荐:这篇文章

同系列文章:

相关文献

相关文章
相关标签/搜索