新推出 Service Worker 預設為

傑夫波斯尼克
Jeff Posnick

tl;dr

從 Chrome 68 版開始,HTTP 快取將不再執行檢查 Service Worker 指令碼更新的 HTTP 要求。這種做法可以解決常見的開發人員問題點,在服務工作站指令碼上設定惡意的 Cache-Control 標頭可能會導致更新延遲。

如果您已透過 Cache-Control: max-age=0 提供 /service-worker.js 指令碼來停用 HTTP 快取功能,則新的預設行為不會產生任何變更。

此外,從 Chrome 78 版開始,系統會針對透過 importScripts() 載入服務工作站的指令碼,套用位元組與位元組的比較。任何對已匯入指令碼所做的變更都會觸發服務工作站更新流程,就像對頂層 Service Worker 做出的變更一樣。

背景

每次您瀏覽到 Service Worker 範圍內的新網頁時,請從 JavaScript 明確呼叫 registration.update(),或服務工作站透過 pushsync 事件「喚醒」時,瀏覽器將同時要求原本傳入 navigator.serviceWorker.register() 呼叫的 JavaScript 資源,藉此尋找服務 Worker 指令碼的更新。

基於本文目的,假設其網址為 /service-worker.js,且其中包含對 importScripts() 的單一呼叫,該呼叫會載入在服務工作站中執行的其他程式碼:

// Inside our /service-worker.js file:
importScripts('path/to/import.js');

// Other top-level code goes here.

異動內容

在 Chrome 68 之前的版本中,/service-worker.js 的更新要求會透過 HTTP 快取提出 (大多數擷取作業都是採用)。這表示如果指令碼原本是以 Cache-Control: max-age=600 傳送,接下來 600 秒 (10 分鐘) 內的更新就不會傳送至網路,因此使用者可能無法接收到最新版本的 Service Worker。不過,如果 max-age 超過 86400 (24 小時),系統會將其視為 86400,避免使用者永遠卡在特定版本。

自 68 版開始,在要求更新 Service Worker 指令碼時,系統會忽略 HTTP 快取,因此現有網頁應用程式可能會發現服務工作站指令碼的要求頻率上升。針對 importScripts 的要求仍會透過 HTTP 快取傳送。不過,這只是預設值,新的註冊選項 updateViaCache 可以讓您控制這個行為。

updateViaCache

開發人員現在可以在呼叫 navigator.serviceWorker.register() 時傳入新選項:updateViaCache 參數。這可使用以下三個值之一:'imports''all''none'

在發出 HTTP 要求來檢查已更新的 Service Worker 資源時,這些值會決定瀏覽器的標準 HTTP 快取是否會運作以及播放方式。

  • 如果設為 'imports',在檢查 /service-worker.js 指令碼的更新時,系統一律不會查詢 HTTP 快取,但在擷取任何匯入的指令碼 (在本範例中為 path/to/import.js) 時會諮詢 HTTP 快取。這是預設值,且與 Chrome 68 版起的行為相符。

  • 如果設為 'all',在同時針對頂層 /service-worker.js 指令碼及服務工作站內匯入的任何指令碼 (例如 path/to/import.js) 傳送要求時,系統會查詢 HTTP 快取。這個選項對應於 Chrome 68 以下版本的 Chrome 先前行為。

  • 設為 'none' 時,當您要求頂層 /service-worker.js 或任何匯入的指令碼 (例如假設的 path/to/import.js) 時,系統不會查詢 HTTP 快取。

舉例來說,以下程式碼會註冊服務工作站,確保在檢查 /service-worker.js 指令碼的更新或任何透過 /service-worker.js 中的 importScripts() 參照的任何指令碼時,一律不會查詢 HTTP 快取:

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/service-worker.js', {
    updateViaCache: 'none',
    // Optionally, set 'scope' here, if needed.
  });
}

檢查已匯入指令碼是否有更新

在 Chrome 78 版之前,系統都只會擷取透過 importScripts() 載入的任何 Service Worker 指令碼一次 (先檢查 HTTP 快取,或是透過網路檢查,視 updateViaCache 設定而定)。初始擷取作業完成後,瀏覽器會將資料儲存在瀏覽器內部,而且永遠不會重新擷取。

如要強制已安裝的 Service 工作站接收已匯入指令碼的變更,唯一的方法就是變更指令碼的網址,通常是透過新增 semver 值 (例如 importScripts('https://example.com/v1.1.0/index.js')),或是加入內容雜湊 (例如 importScripts('https://example.com/index.abcd1234.js'))。變更已匯入流程的網址也會觸發更新流程的工作站內容變更,而服務工作指令碼內容也會跟著改變

從 Chrome 78 版開始,每次為頂層 Service Worker 檔案執行更新檢查時,都會同時進行檢查,以確認任何匯入的指令碼內容是否已變更。視使用的 Cache-Control 標頭而定,如果 updateViaCache 設為 'all''imports' (此為預設值),HTTP 快取可能會執行這些匯入的指令碼檢查;如果 updateViaCache 設為 'none',系統可能會直接針對網路執行檢查。

如果與服務工作站先前儲存的指令碼相比,如果更新檢查造成位元組逐位元組的差異,那麼即使頂層 Service Worker 檔案維持不變,也會觸發完整的 Service Worker 更新流程。

Chrome 78 的行為與近幾年前在 Firefox 56 中實作的 Firefox 相同。Safari 也已實作這個行為。

開發人員需要做什麼?

如果您透過 Cache-Control: max-age=0 (或類似值) 提供 /service-worker.js 指令碼,並藉此有效停用 HTTP 快取,那麼您應該不會看到任何因新的預設行為而發生變化。

如果您在啟用 HTTP 快取的情況下提供 /service-worker.js 指令碼 (無論是刻意或只是代管環境的預設選項),那麼您可能會發現針對伺服器發出的 /service-worker.js 額外 HTTP 要求,這些要求就是之前由 HTTP 快取執行的要求。如果要繼續允許 Cache-Control 標頭值影響 /service-worker.js 的更新間隔,您必須在註冊 Service Worker 時開始明確設定 updateViaCache: 'all'

由於舊版瀏覽器可能會較常使用舊版瀏覽器,因此,即使新版瀏覽器可能會忽略這類標頭,我們仍建議您繼續針對 Service Worker 指令碼設定 Cache-Control: max-age=0 HTTP 標頭。

開發人員可以藉此機會決定是否要立即將匯入的指令碼選擇不採用 HTTP 快取,並視情況將 updateViaCache: 'none' 新增至其服務工作站註冊項目。

提供匯入的指令碼

從 Chrome 78 版開始,開發人員可能會發現透過 importScripts() 載入的資源收到更多連入 HTTP 要求,因為系統會立即檢查這些要求是否更新。

如要避免這個額外的 HTTP 流量,請在提供網址中包含 semver 或雜湊的指令碼時設定長效的 Cache-Control 標頭,並依賴 'imports' 的預設 updateViaCache 行為。

或者,如果您「想要」檢查匯入的指令碼經常更新,請務必使用 Cache-Control: max-age=0updateViaCache: 'none' 提供這類指令碼。

其他資訊

我們都建議 Jake Archibald 為所有開發人員提供「Service Worker Lifecycle」和「快取最佳做法和 max-age 無障礙化方法」的內容。