新推出 Service Worker 預設為

tl;dr

從 Chrome 68 開始,根據預設,檢查 Service Worker 指令碼更新的 HTTP 要求將不再由 HTTP 快取執行。這個方法可以解決常見的開發人員問題,在 Service Worker 指令碼上設定意外的 Cache-Control 標頭,可能會導致更新延遲。

如果您已透過 Cache-Control: max-age=0 提供 /service-worker.js 指令碼,藉此停用 HTTP 快取功能,應該不會看到任何因新的預設行為而發生的變化。

此外,自 Chrome 78 版起,系統會針對透過 importScripts() 在 Service Worker 中載入的指令碼,進行位元組比較。對匯入的指令碼所做的任何變更都會觸發服務工作站更新流程,就像變更頂層 Service Worker 一樣。

背景

每次您前往位於 Service Worker 範圍內的新頁面、從 JavaScript 明確呼叫 registration.update(),或者當 Service Worker 透過 pushsync 事件「喚醒」時,瀏覽器會同時要求 JavaScript 資源,以尋找原本傳遞至 navigator.serviceWorker.register() 呼叫的 JavaScript 資源,以尋找 Service Worker 指令碼的更新。

為達成本文的目的,我們假設其網址是 /service-worker.js,且包含對 importScripts() 的單一呼叫,可載入在 Service Worker 中執行的其他程式碼:

// 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 以下版本的舊行為。

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

舉例來說,以下程式碼會註冊 Service Worker,並確認在檢查 /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 Worker 來強制更新已匯入的指令碼變更,唯一的方法就是變更指令碼的網址,這通常是加入 semver 值 (例如 importScripts('https://example.com/v1.1.0/index.js')),或是加入內容的雜湊值 (例如 importScripts('https://example.com/index.abcd1234.js'))。變更匯入的網址後,頂層 Service Worker 指令碼的內容會觸發更新流程

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

如果更新匯入指令碼的更新檢查,結果與先前由 Service Worker 儲存的資料不同,就會觸發完整的服務工作站更新流程,即使頂層 Service Worker 檔案維持不變也一樣。

Chrome 78 的行為與多年前的 Firefox 56 實作相同。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' 加入 Service Worker 登錄中。

提供匯入的指令碼

從 Chrome 78 開始,由於現在要檢查更新,開發人員可能會發現透過 importScripts() 載入的資源收到更多傳入 HTTP 要求。

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

或者,如果您「要」檢查匯入的指令碼是否有頻繁更新,請務必使用 Cache-Control: max-age=0 提供這些指令碼,或是使用 updateViaCache: 'none'

其他資訊

凡是在網路部署任何內容的開發人員,都建議參閱「The Service Worker 生命週期」和「快取最佳做法和上限作業」這兩篇文章。