前端日拱一卒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

雖發表於此,卻畢竟爲一人之言,又是每日學有所得之筆記,內容未必詳實,看官老爺們還望海涵。

相關文章
相關標籤/搜索