serviceworker 離線緩存化(附源碼)

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
    })
  )
})

請參考: 源碼地址

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章