serviceWorker 离线缓存

308 阅读2分钟

Service worker是一个注册在指定源和路径下的事件驱动worker。它采用JavaScript控制关联的页面或者网站,拦截并修改访问和资源请求,细粒度地缓存资源。你可以完全控制应用在特定情形(最常见的情形是网络不可用)下的表现。

Service worker运行在worker上下文,因此它不能访问DOM。相对于驱动应用的主JavaScript线程,它运行在其他线程中,所以不会造成阻塞。它设计为完全异步,同步API(如XHRlocalStorage)不能在service worker中使用。

出于安全考量,Service workers只能由HTTPS承载,毕竟修改网络请求的能力暴露给中间人攻击会非常危险。在Firefox浏览器的用户隐私模式,Service Worker不可用。

如何使用如下:

1、html 引入代码非https不能使用

if ('serviceWorker' in navigator && location.protocol == 'https:') {
  navigator.serviceWorker.register('/serviceWorker.js?v=201911281608')
}
注意version 每次更新如何要替换版本来控制serviecWorker.js 文件的更新     

2、serviceWorker.js 代码如何下

// 当前缓存版本的唯一标识符,用当前时间代替
var VERSION = "201911281608"

// 当前缓存白名单,在新脚本的 install 事件里将使用白名单里的 key

var cacheFileList = [
  "staticPath/static/??vue/2.6.10/index.js,js/1.0.0/polyfill/index.js",
  "staticPath/static/??js/1.0.1/utils/index.js,js/1.0.1/ajax/index.js,js/1.0.0/store/index.js,js/1.0.1/request/index.js,js/1.0.5/scale/index.js",
  "staticPath/static/??js/1.0.3/toast/index.js,js/1.0.1/loading/index.js",
  "staticPath/static/??css/1.1.1/main.css,css/1.1.1/ui-component.css"
]
self.addEventListener('install', (event) =>  {
  // 等待所有资源缓存完成时,才可以进行下一步
  event.waitUntil(
    caches.open(VERSION)
    .then((cache) => {
      // 要缓存的文件 URL 列表
      return cache.addAll(cacheFileList)
    })
    .then(() => self.skipWaiting())
    .then(() => console.log('installation complete! version: ' + VERSION))
  )
})

self.addEventListener('activate', (event) =>  {
  event.waitUntil(
    caches.keys().then((cacheNames) => {
      return Promise.all(
        cacheNames
        .filter(key => !key.startsWith(VERSION))  // 过滤不需要删除的资源
        .map(key => caches.delete(key))  // 删除旧版本资源,返回为 Promise 对象
      )
    }).then(() => {
      console.log('removeOldCache completed.');
    })
  )
})

var isCORSRequest = function(url) {
  return url.indexOf(location.host) === -1
}

var isNeedCache = function(url) {
  var CACHE_HOST = ['img.piaoniu.com', 'm.img.whqietu.com', 'm.static.whqietu.com', 'assets.piaoniu.com']
  return CACHE_HOST.some((host) => {
    return url.indexOf(host) !== -1
  })
}
self.addEventListener('fetch',(event) => {
  var url = event.request.url
  // Ignore not GET https request.
  if (event.request.method !== 'GET' || new URL(url).protocol !== 'https:' || !isNeedCache(url)) {
    return
  }
  var request = isCORSRequest(url) ? new Request(url, {mode: 'cors'}) : event.request
  event.respondWith(
    caches.match(request)
    .then((response) => {
      if (response) {
        return response
      }

      // 因为 event.request 流已经在 caches.match 中使用过一次,
      // 那么该流是不能再次使用的。我们只能得到它的副本,拿去使用。
      var fetchRequest = request.clone()

      // fetch 的通过信方式,得到 Request 对象,然后发送请求
      return fetch(fetchRequest)
        .then((httpRes) => {
          // 检查是否成功
          if(!httpRes || httpRes.status !== 200 || (httpRes.type !== 'basic' && httpRes.type !== 'cors')) {
            return httpRes
          }

          // 如果成功,该 response 一是要拿给浏览器渲染,而是要进行缓存。
          // 不过需要记住,由于 caches.put 使用的是文件的响应流,一旦使用,
          // 那么返回的 response 就无法访问造成失败,所以,这里需要复制一份。
          var responseToCache = httpRes.clone()
          caches.open(VERSION)
          .then((cache) => {
            cache.put(request, responseToCache)
          })

          return httpRes
        }
      )
    })
  )
})