前端日拱一卒D3——Service Worker

前言

余为前端菜鸟,感姿式水平匮乏,难观前端之大局。遂决定循前端知识之脉络,以兴趣为引,辅以几分坚持,望于己能解惑致知、于同道能助力一二,岂不美哉。html

本系列代码及文档均在 此处前端

简介

service worker是一个由来已久的HTML5 API,旨在建立持久的离线缓存。相似于web worker,都是在浏览器后台单独开的一个线程内工做,能够向客户端推消息,不可操做dom。git

service worker有本身的worker context,能够拦截请求和返回,缓存文件,必须在HTTPS或者本地环境下运行,异步实现,内部大部分经过Promise实现github

依赖Cache API,依赖FTML5 fetch API,依赖Promise实现离线缓存功能web

生命周期

parsed -> installing -> installed -> activating -> activated -> redundantchrome

引用

注册

注册在主线程中进行api

if (navigator.serviceWorker) {
  // register异步方法
  navigator.serviceWorker.register('./sw.js', { scope: '/' }).then(() => {
    console.log('sw service worker 注册成功')
    console.log('parsed ----> installing')
  }).catch((err) => {
    console.log(err)
  })
}
复制代码
  • 注册时的scope可选,默认是sw.js所在目录,表示该worker能够接收该目录下的全部fetch事件
  • 每次调用egister会判断worker是否已注册再进行处理
  • 注册成功的worker有了本身的worker context,此时表示worker的脚本被成功解析,转入installing状态

安装

在worker的脚本文件中监听install事件,能够维护初始缓存列表浏览器

self.addEventListener('install', (event) => {
  // 确保安装完成前完成下面的操做
  event.waitUntil(
    // 建立一个cache
    caches.open('v1').then((cache) => {
      // cache是缓存实例
      // 调用该实例的addAll方法提早加载相关文件进缓存
      return cache.addAll([
        '/html/test.js',
        '/html/default.html'
      ])
    })
    console.log('installing ----> installed ----> activating')
  )
  // self.skipWaiting() 直接进入activate
})
复制代码



激活

worker安装完成后会转为installed/activating状态,在知足如下条件之一时能够转为activate状态缓存

  • 没有active worker在运行或者旧的worker被释放(页面关闭)
  • 调用self.skipWaiting()跳过waiting,进入activate状态

activated状态会触发activate回调,在worker监本中监听activate事件app

self.addEventListener('activate', function (event) {
  event.waitUntil(
    caches.keys().then(function (cacheNames) {
      return Promise.all(
        cacheNames.filter(function (cacheName) {
          return cacheName != 'v1';
        }).map(function (cacheName) {
          // 清除旧的缓存
          return caches.delete(cacheName);
        })
      )
    })
    console.log('activating ----> activated')
  )
  // self.clients.claim() 获取页面控制权,旧的worker失效
})
复制代码

激活后的控制

激活后能够控制页面行为,能够处理功能事件,如fetch, push, message

符合worker的scope内的资源请求都会触发fetch被控制

self.addEventListener('fetch', function (event) {
  // 利用respondWith劫持响应
  event.respondWith(
    caches.open('v1').then(function (cache) {
      return cache.match(event.request).then(function (response) {
        // match到则返回不然直接请求
        return response || fetch(event.request).then(function (response) {
          // 404抛错到catch处理
          if (response.status === 404) {
            throw new Error('nothing')
          }
          // 将response做为value存入cache
          // install时能够进行缓存
          // 劫持fetch时也能够进行动态资源缓存
          cache.put(event.request, response.clone());
          return response;
        }).catch((err) => {
          // 返回cache storage里存的默认页面
          return caches.match('/html/default.html');
        });
      });
    })
  );
});
复制代码

localStorage是同步的,因此不能用于service worker内的存储,IndexedDB能够用

废弃状态

安装失败、激活失败、被新的worker取代时转入废弃状态

chrome中查看

更新

当service worker脚本内容更新时,会安装新的文件并触发install,转入installed/waiting状态。此时旧的worker仍处于激活状态,在页面关闭后会被废弃,此后新开页面里新的worker才会生效

上图为更改sw.js后刷新页面的结果,关闭页面重开后1497将是激活中的worker

强制更新和检查更新

通常为24小时

自动更新worker

// 跳过等待,直接进入activate
self.addEventListener('install', function (event) {
    event.waitUntil(self.skipWaiting());
});
// actived以前更新客户端
self.addEventListener('activate', function (event) {
  event.waitUntil(
    Promise.all([
      // 更新客户端全部的service worker
      self.clients.claim(),
      // 清理旧版本
      caches.keys().then(function (cacheList) {
        return Promise.all(
          cacheList.map(function (cacheName) {
            if (cacheName !== 'v1') {
              return caches.delete(cacheName);
            }
          })
        )
      })
    ])
  )
)}
复制代码

手动更新

主线程内每次注册时进行更新

var version = '1.0';
navigator.serviceWorker.register('./sw.js').then(function (reg) {
  if (localStorage.getItem('sw_version') !== version) {
    // reg.update
    reg.update().then(function () {
      localStorage.setItem('sw_version', version)
    });
  }
});
复制代码

debug时更新

self.addEventListener('install', function () {
  if (ENV === 'development') {
    // 每次刷新页面从新注册安装时直接进入activate,确保最新
    self.skipWaiting();
  }
});
复制代码

cacheStorage

cacheStorage是在serviceworker规范中定义的接口,咱们可使用全局的caches访问cacheStorage

caches经常使用api有: open, match, delete, has, keys

cache storage

实例

代码位于github

steps

  • 本地利用koa-static建一个静态页面的server

    const Koa = require('koa')
    const path = require('path')
    const static = require('koa-static')
    const app = new Koa()
    const staticPath = './frontend/basic'
    app.use(static(
        path.join(__dirname, staticPath)
    ))
    复制代码
  • 页面中插入脚本

    window.addEventListener('load', function () {
        if (navigator.serviceWorker) {
            navigator.serviceWorker.register('./sw.js').then(() => {
                console.log('sw service worker 注册成功')
            }).catch((err) => {
                console.log(err)
            })
        }
    })
    复制代码
  • sw.jsgithub

最终效果

  • offline模式下,能够从cache中读取文件缓存

    from service worker

  • 不存在的资源路径返回缓存好的default.html

  • 二次访问缓存list内的资源时劫持请求和响应,返回缓存内容

    cache storage

虽发表于此,却毕竟为一人之言,又是每日学有所得之笔记,内容未必详实,看官老爷们还望海涵。

相关文章
相关标签/搜索