Service Worker

Service workers 本质上充当Web应用程序与浏览器之间的代理服务器,它会在浏览器启动后运行

用处

  1. 缓存/预存api静态资源
  2. web离线,充当服务器
  3. 拦截请求
  4. 消息推送

使用限制

  1. 兼容性
  2. 只可以用于https域名下(本地也可以)
  3. 不可操作dom
  4. 不可使用localStorage

兼容


serviceWorker被置于navigator对象上,判断其存在则可以使用

if (navigator.serviceWorker) {
  ....
}

注册


serviceWorker.register(path, {scope: './'})

  • path:serviceWorker脚本路径
  • scope:指当前serviceWorker的作用范围,上面是用于整个网站

返回值一个promise对象,成功得到ServiceWorkerRegistration对象

navigator.serviceWorker.register('server.js', { scope: './' }).
        then((reg) => {
            console.log(reg)
        }).catch((err) => {
            console.log(err)
        })

注:

  1. 如果scope是根目录,那么server.js必须在根目录
  2. 同源下可以注册多个sw,但是scope必须不同

下载


注册成功后进入脚本下载,之后刷新或者自动在24小时内会重新下载脚本,检查脚本是否更新

安装


下载或更新脚本后会进行安装,触发install事件,完成后执行event.waitUntil(promise)

脚本中使用this/self直接访问相关接口

// server.js
self.addEventListener('install', function (event) {
  console.log('Service Worker install');
});

激活


第一次安装成功后或新脚本可用后,会进行激活,触发activate事件

这个事件主要用于清理旧缓存和旧的service worker的一些东西

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

  event.waitUntil(
    caches.keys().then(function(keyList) {
      return Promise.all(keyList.map(function(key) {
        if (cacheWhitelist.indexOf(key) === -1) {
          return caches.delete(key);
        }
      }));
    })
  );
});

注:

  1. 更换脚本后,新脚本会在后台安装,等待旧脚本不再被页面使用才会激活新脚本,刷新旧脚本依然会使用,只有完全退出,然后重新进入网站才会正式使用
    可以使用skipWaiting跳过等待,并立马控制页面
self.addEventListener('install', event => {
  self.skipWaiting();

  event.waitUntil(
    // ...
  );
});

skipWaiting()意味着新 SW 控制了之前用旧 SW 获取的页面,也就是说你的页面有一部分资源是通过旧 SW 获取,剩下一部分是通过新 SW 获取的,如果这样做会给你带来麻烦,那就不要用skipWaiting()

  1. 初次激活后(包括第一次安装激活),当前作用域内的页面并未受控(意味着,fetch拦截等都不生效),需要等到刷新或者使用clients.claim()来让页面立马受到控制
self.addEventListener('activate', function(event) {
  event.waitUntil(self.clients.claim());
});

请求拦截


使用fetch事件可以监听http请求,也可以使用respondWith(promise)方法或 Response 对象自定义响应内容

this.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request) // 从缓存中读取直接返回
  );
});

缓存


sw使用CacheStroage存储请求,接口为 caches

CacheStroage

  • caches.open('xx') 打开缓存库xx(无则新建),返回promise,打开后才可进行后续cache操作
  • caches.delete(xx) 删除缓存库xx(超过限制,浏览器会自动删除)
  • caches.match(request) 从缓存中查找请求,返回promise,成功拿到响应数据
  • cache.put(request, responeseClone) 缓存一个请求,存储响应应该是复制的responese.clone()(因为一个请求和响应流只能被读取一次)
  • 其他api

一个缓存所有请求的例子:

this.addEventListener('fetch', function (event) {
  event.respondWith( // 拦截响应
    caches.match(event.request).then(res => { // 从缓存中查找,有则返回,无则请求并存储
      return res ||
        fetch(event.request)
          .then(responese => {
            const responeseClone = responese.clone(); // 复制一份响应数据
            caches.open('xx').then(cache => { // 打开存储库 xx
              cache.put(event.request, responeseClone); // 请求响应存入 xx
            })
            return responese;
          })
          .catch(err => { // 缓存读取失败,请求也失败,应该在这里做兼容处理,比如显示加载失败检查网络
            console.log(err);
          });
    })
  )
});

// 作者:十月七秋
// 链接:https://juejin.im/post/5b06a7b3f265da0dd8567513

通信


页面到sw

  • 页面发送
    serviceWorker.controller.postMessage(msg) 进行信息发送
// sw 注册后才能发送
navigator.serviceWorker.controller.postMessage("this message is from page");
  • sw接收
    监听message事件获取发送的信息
this.addEventListener('message', function (event) {
  console.log(event.data); // this message is from page
});

如果注册的scope不是根路径,使用sw实例.active.postMessage

navigator.serviceWorker.register('./sw.js', { scope: './sw' })
    .then(function (reg) {
      reg.active.postMessage("this message is from page, to sw");
    })

sw到页面

  • sw发送
    message事件中发送
this.addEventListener('message', function (event) {
  event.source.postMessage('this message is from sw.js, to page');
});
  • 页面接收
    接收都使用message事件
navigator.serviceWorker.addEventListener('message', function (e) {
  console.log(e.data); // this message is from sw.js, to page
});

sw 到 sw

Message Channel
https://juejin.im/post/5b06a7b3f265da0dd8567513#heading-5

插件

https://developers.google.com/web/tools/workbox
https://zoumiaojiang.com/article/amazing-workbox-3/
https://juejin.im/post/5d47f5c45188255d2a78af38#heading-6
https://lavas.baidu.com/guide


参考资料
https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle#waiting
MDN Service Worker
MDN SW 实例
Service Worker最佳实践
Service Worker 全面进阶
网站渐进式增强体验(PWA)改造:Service Worker 应用详解
Service Worker —这应该是一个挺全面的整理

你可能感兴趣的:(Service Worker)