PWA

Service Worker

生命周期

先来看一下一个service worker的运行周期

1. 安装install
2. 激活activate
3. 监听fetch和message事件

默认范围:

navigator.serviceWorker.register('/assets/sw.js').then(function (registration){
    console.log('service worker 注册成功', registration.scope);
}).catch(function (err) {
    console.log('servcie worker 注册失败')
});
// 默认scope是/assets

修改默认范围:

navigator.serviceWorker.register("/assets/sw.js", { scope: "/" })要设置scope='/'response返回sw.js要带上Service-Worker-Allowed='/'的头。这样才能注册成功

navigator.serviceWorker.register("/assets/sw.js", { scope: "/" }).catch(() => {
  console.log('servcie worker 注册失败')
});

不在范围内的请求,fetch拦截不到:

如scope=’/assets’,请求/index.js,fetch是拦截不到的。因此,这些请求不能通过fetch拦截来缓存。可以用cache.addAll()添加缓存。

fetch事件

在页面发起http请求时,service worker可以通过fetch事件拦截请求,并且给出自己的响应。

提供了Request、Response对象,如果做过后端开发,对Request、Response应该比较熟悉。前端要发起请求可以通过url发起,也可以使用Request对象发起,而且Request可以复用。但是Response用在哪里呢?在service worker出现之前,前端确实不会自己给自己发消息,但是有了service worker,就可以在拦截请求之后根据需要发回自己的响应,对页面而言,这个普通的请求结果并没有区别,这是Response的一处应用。而且权限很大,可以拦截chrome插件的网络请求(可以以此推断你装了什么插件)。对于我们开发,缓存插件的请求是没什么意义的,一般都过滤掉。

var hostReg = /(localhost|o8ci6tgz8.qnssl.com)/;

self.addEventListener('fetch', function (evt) {
  console.log(evt.request.url);
  evt.respondWith(
    caches.match(evt.request).then(function (response) {
      if (response) {
        return response;
      }
      var request = evt.request.clone();
      return fetch(request).then(function (response) {
        if (response && response.status === 200 && response.url.match(hostReg)) {
          var responseClone = response.clone();
          caches.open(VERSION).then(function (cache) {
            // console.log(evt.request.url);
            cache.put(evt.request, responseClone);
          });
        }
        return response;
      });
    })
  )
});

message事件

页面和serviceWorker之间可以通过posetMessage()方法发送消息,发送的消息可以通过message事件接收到。

这是一个双向的过程,页面可以发消息给service worker,service worker也可以发送消息给页面,由于这个特性,可以将service worker作为中间纽带,使得一个域名或者子域名下的多个页面可以自由通信。

离线缓存和CDN

现在,我们的网站一般都是单页应用。一般html放在服务器,其他资源文件放CDN。和平时不同,不要使用默认scope,而是设置CDN为scope。一般其他资源文件太多,全部列出来困难且不易维护,使用fetch拦截,来缓存。然后,只需指定缓存[‘/‘]。

安全

serviceWorker权限是太大的,因此我们要保证页面没有XSS漏洞。一旦注册了别人的serviceWorker,后果可想而知。不过,现在的框架都能帮我们处理XSS问题。