Service Worker 入門指南

來自 「何天翔」 同學的內部分享。

Service Worker 簡介

Service Workers 本質上是一種能在瀏覽器後臺運行的獨立線程,它能夠在網頁關閉後持續運行,能夠攔截網絡請求並根據網絡是否可用來採取適當的動作、更新來自服務器的的資源,從而實現攔截和加工網絡請求、消息推送、靜默更新、事件同步等一系列功能,是 PWA 應用的核心技術之一。

與普通 JS 運行環境相比,Service Workers 有如下特點:

本文將從應用角度,簡單彙總下 Service Workers 幾個核心概念,包括:API、生命週期、waitUntil 機制、調試等。

生命週期

Service Worker 的生命週期完全獨立於網頁。生命週期 (install -> waiting -> activate -> fetch):

其中, install 事件是 Service Worker 獲取的第一個事件,並且只發生一次。

主要邏輯 & API

  // register
  if ('serviceWorker' in navigator) {
        // 由於 127.0.0.1:8000 是所有測試 Demo 的 host
        // 爲了防止作用域污染,將安裝前註銷所有已生效的 Service Worker
        navigator.serviceWorker.getRegistrations()
          .then(regs ={
            for (let reg of regs) {
              reg.unregister()
            }
            navigator.serviceWorker.register('./sw.js')
          })
      }
// sw.js
console.log('service worker 註冊成功')

self.addEventListener('install'() ={
  // 安裝回調的邏輯處理
  console.log('service worker 安裝成功')
})

self.addEventListener('activate'() ={
  // 激活回調的邏輯處理
  console.log('service worker 激活成功')
})

self.addEventListener('fetch'event ={
  console.log('service worker 抓取請求成功: ' + event.request.url)
})

「waitUntil 機制」

參考:https://developer.mozilla.org/zh-CN/docs/Web/API/ExtendableEvent/waitUntil

ExtendableEvent.waitUntil() 方法告訴事件分發器該事件仍在進行。這個方法也可以用於檢測進行的任務是否成功。在服務工作線程中,這個方法告訴瀏覽器事件一直進行,直至 promise resolve,瀏覽器不應該在事件中的異步操作完成之前終止服務工作線程。

「skipWaiting」

Service Worker 一旦更新,需要等所有的終端都關閉之後,再重新打開頁面才能激活新的 Service Worker,這個過程太複雜了。通常情況下,開發者希望當 Service Worker 一檢測到更新就直接激活新的 Service Worker。如果不想等所有的終端都關閉再打開的話,只能通過 skipWaiting 的方法了。

Service Worker 在全局提供了一個 skipWaiting() 方法,skipWaiting() 在 waiting 期間調用還是在之前調用並沒有什麼不同。一般情況下是在 install 事件中調用它。

「clients.claim() 方法」

如果使用了 skipWaiting 的方式跳過 waiting 狀態,直接激活了 Service Worker,可能會出現其他終端還沒有受當前終端激活的 Service Worker 控制的情況,切回其他終端之後,Service Worker 控制頁面的效果可能不符合預期,尤其是如果 Service Worker 需要動態攔截第三方請求的時候。

爲了保證 Service Worker 激活之後能夠馬上作用於所有的終端,通常在激活 Service Worker 後,通過在其中調用 self.clients.claim() 方法控制未受控制的客戶端。self.clients.claim() 方法返回一個 Promise,可以直接在 waitUntil() 方法中調用,如下代碼所示:

self.addEventListener('activate'event ={
  event.waitUntil(
    self.clients.claim()
      .then(() ={
        // 返回處理緩存更新的相關事情的 Promise
      })
  )
})

如何處理 Service Worker 的更新

方法一:skipWaiting

問題:同一個頁面,前半部分的請求是由 sw.v1.js 控制,而後半部分是由 sw.v2.js 控制。這兩者的不一致性很容易導致問題,甚至網頁報錯崩潰

方法二:skipWaiting + 刷新

let refreshing = false
navigator.serviceWorker.addEventListener('controllerchange'() ={
  if (refreshing) {
    return
  }
  refreshing = true;
  window.location.reload();
});

問題:毫無徵兆的刷新頁面的確不可接受,影響用戶體驗

方法三:給用戶一個提示

大致的流程是:

  1. 瀏覽器檢測到存在新的(不同的)SW 時,安裝並讓它等待,同時觸發 updatefound 事件

  2. 我們監聽事件,彈出一個提示條,詢問用戶是不是要更新 SW

  3. 如果用戶確認,則向處在等待的 SW 發送消息,要求其執行 skipWaiting 並取得控制權

  4. 因爲 SW 的變化觸發 controllerchange 事件,我們在這個事件的回調中刷新頁面即可

問題:

如何調試

爲了更熟練的運用 Chrome Devtools 調試 Service Worker,首先需要熟悉以下這些選項:

總結

完整流程

應用場景

基於 service worker 可以實現攔截和處理網絡請求、消息推送、靜默更新、事件同步等服務。

  1. 離線緩存:配合 CacheStorage 可以將應用中不變化的資源或者很少變化的資源長久的存儲在用戶端,提升加載速度、降低流量消耗、降低服務器壓力,提高請求速度,讓用戶體驗更加絲滑

  2. 消息推送:激活沉睡的用戶,推送即時消息、公告通知,激發更新等。如 web 資訊客戶端、web 即時通訊工具、h5 遊戲等運營產品。

  3. 事件同步:確保 web 端產生的任務即使在用戶關閉了 web 頁面也可以順利完成。如 web 郵件客戶端、web 即時通訊工具等。

  4. 定時同步:週期性的觸發 Service Worker 腳本中的定時同步事件,可藉助它提前刷新緩存內容

  5. 結合 CacheStorage、 Push API 和 Notification API

參考鏈接:

  • https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API

  • https://juejin.cn/post/6844903792522035208

  • https://www.simicart.com/blog/pwa-service-worker/

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/bYtTRjqQylYKO9D1jbadSA