serviceworker 缓存离线化

概述

Service Worker 是HTML5 的一个新特性,主要用来做持久的离线缓存

项目介绍

本项目在第一次安装serverworker之后,可以在控制台看到以下信息:

在这里插入图片描述

查看对应请求的静态资源信息:

可以看到,第一次安装serviceworker时,读取到的静态资源并没有缓存。

在这里插入图片描述
刷新浏览器之后,我们再看一下这些静态资源:

可以看到,静态资源以及被serviceworker缓存起来了。

在这里插入图片描述

我们再来查看当前serviceworker安装情况:

可以看到,serviceworker已经处于激活状态。

在这里插入图片描述

最后,看一下离线功能的效果:

是不是很神奇,在离线状态下我们的页面也是能够展示出数据的。 其中,离线的原理就是利用了serviceworker中,fetchcacheStorage这两个接口,将请求进行拦截,将响应进行缓存

在这里插入图片描述

作用

这个 API 的唯一目的就是解放主线程,Web Worker 是脱离在主线程之外的,将一些复杂的耗时的活交给它干,完成后通过 postMessage 方法告诉主线程,而主线程通过 onMessage 方法得到 Web Worker 的结果反馈。

功能和特性

  • Service Worker拥有自己独立的 worker 线程,独立于当前网页线程
  • 离线缓存静态资源
  • 拦截代理请求和响应
  • 可自定义响应内容
  • 可以通过postMessage向主线程发送消息
  • 无法直接操作DOM
  • 必须在HTTPS环境下工作或 localhost / 127.0.0.1 (自身安全机制)
  • 通过Promise异步实现
  • Service Worker安装(installing)完成后,就会一直存在,除非手动卸载(unregister)

生命周期

在这里插入图片描述

Service Worker 的生命周期完全独立于网页

  • 注册 (register)
  • 安装 (install)
  • 激活 (activate)

通常使用 service worker 只需要以下几个步骤:

  • 检测是否支持serivceworker

首先,检测当前环境是否支持 service worker,可以使用 'serviceWorker' in navigator 进行检测。

  • 注册

如果支持,可以使用 navigator.serviceWorker.register('./sw.js'),在当前主线程中注册 service worker。如果注册成功,service worker 则在 ServiceWorkerGlobalScope环境中运行; 需要注意的是: 当前环境无法操作DOM,且和主线程之间相互独立(即线程之间不会相互阻塞)。

  • 安装

然后,后台开始安装service worker,一般在此过程中,开始缓存一些静态资源文件。

  • 激活

安装成功之后,准备进行激活 service worker,通常在激活状态下,主要进行缓存清理,更新service worker等操作。

  • 使用

激活成功后,,service worker 就可以控制当前页面了。需要注意的是,只有在service worker成功激活后,才具有控制页面的能力,一般在第一次访问页面时,service worker第一次创建成功,并没有激活,只有当刷新页面,再次访问之后,才具有控制页面的能力。

源码实现

该源码实现了以下几个功能:

  • 强制更新 通过self.skipWaiting(),如果检测到新的service worker文件,就会立即替换掉旧的。
  • 缓存静态资源 cache.addAll(cacheFiles) 通过这个接口实现
  • 拦截请求 通过监听fetch事件,可以拦截当前页所有请求self.addEventListener('fetch',function(e){})
  • 缓存响应 将响应内容加入缓存cache.put(evt.request, response)
// 缓存静态资源文件列表
let cacheFiles = [
  './test.js',
  './index.html',
  './src/img/yy.png'
]
// serviceworker使用版本
let __version__ = 'cache-v2'

// 缓存静态资源
self.addEventListener('install', function (evt) {
  // 强制更新sw.js
  self.skipWaiting()
  evt.waitUntil(
    caches.open(version).then(function (cache) {
      return cache.addAll(cacheFiles)
    })
  )
})

// 缓存更新
self.addEventListener('active', function (evt) {
  evt.waitUntil(
    caches.keys().then(function (cacheNames) {
      return Promise.all(
        cacheNames.map(function (cacheName) {
          if (cacheName !== version) {
            return caches.delete(cacheName)
          }
        })
      )
    })
  )
})

// 请求拦截
self.addEventListener('fetch', function (evt) {
  console.log('处理fetch事件:', evt.request.url)
  evt.respondWith(
    caches.match(evt.request).then(function (response) {
      if (response) {
        console.log('缓存匹配到res:', response)
        return response
      }
      console.log('缓存未匹配对应request,准备从network获取', caches)
      return fetch(evt.request).then(function (response) {
        console.log('fetch获取到的response:', response)
        caches.open(version).then(function (cache) {
          cache.put(evt.request, response)
          return response
        })
      })
    }).catch(function (err) {
      console.error('fetch 接口错误', err)
      throw err
    })
  )
})

复制代码

请参考: 源码地址

猜你喜欢

转载自juejin.im/post/5c74e5566fb9a049be5e23c0