重點摘要
自 Chrome 68 起,根據預設,檢查服務工作者指令碼更新的 HTTP 要求將不再由 HTTP 快取處理。這可解決常見的開發人員痛點,在服務工作者指令碼中不小心設定 Cache-Control
標頭,可能會導致更新延遲。
如果您已透過 Cache-Control: max-age=0
放送 /service-worker.js
指令碼,並選擇不使用 HTTP 快取,則不會因為新的預設行為而看到任何變更。
此外,從 Chrome 78 開始,系統會透過 importScripts()
將位元組與位元組比較方式套用至在服務工作站中載入的程式碼。對匯入的腳本所做的任何變更都會觸發服務工作單元更新流程,就像變更頂層服務工作單元一樣。
背景
每次您前往服務工作者範圍內的新頁面,從 JavaScript 明確呼叫 registration.update()
,或是服務工作者透過 push
或 sync
事件「喚醒」時,瀏覽器會同時要求最初傳入 navigator.serviceWorker.register()
呼叫的 JavaScript 資源,以便查看服務工作者指令碼的更新內容。
為了方便說明,我們假設網址為 /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 快取提出 (因為大多數擷取都是透過 HTTP 快取提出)。也就是說,如果原先是使用 Cache-Control:
max-age=600
傳送指令碼,則接下來 600 秒 (10 分鐘) 內的更新內容不會傳送到網路,因此使用者可能不會收到最新的服務工作程版本。不過,如果 max-age
大於 86400 (24 小時),系統會將其視為 86400,以免使用者永遠卡在特定版本。
從 68 版開始,在要求服務工作者指令碼更新時,系統會忽略 HTTP 快取,因此現有的網頁應用程式可能會增加服務工作者指令碼要求的頻率。importScripts
的要求仍會透過 HTTP 快取傳送。不過,這只是預設值,我們也提供新的註冊選項 updateViaCache
,可讓您控制這項行為。
updateViaCache
開發人員現在可以在呼叫 navigator.serviceWorker.register()
時傳入新的選項:updateViaCache
參數。這個方法會採用下列三個值之一:'imports'
、'all'
或 'none'
。
這些值會決定瀏覽器的標準 HTTP 快取在發出 HTTP 要求以檢查更新的服務工作者資源時,是否會啟用,以及如何啟用。
如果設為
'imports'
,系統在檢查/service-worker.js
指令碼的更新時,就不會查詢 HTTP 快取,但在擷取任何匯入的指令碼 (在本例中為path/to/import.js
) 時,就會查詢。這是預設值,與 Chrome 68 開始的行為相符。當此值設為
'all'
時,系統會在要求頂層/service-worker.js
指令碼和服務 worker 中匯入的任何指令碼 (例如path/to/import.js
) 時,查詢 HTTP 快取。這個選項對應的是 Chrome 68 之前的舊版行為。如果設為
'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()
載入的任何服務工作者指令碼都只會擷取一次 (先檢查 HTTP 快取,或透過網路,視 updateViaCache
設定而定)。在初始擷取作業完成後,瀏覽器會將該資料儲存在內部,並不會重新擷取。
要強制已安裝的服務工作者擷取匯入指令碼的變更,唯一的方法就是變更指令碼的網址,通常是新增semver 值 (例如 importScripts('https://example.com/v1.1.0/index.js')
),或是加入內容的雜湊 (例如 importScripts('https://example.com/index.abcd1234.js')
)。變更匯入的網址會導致頂層服務工作者指令碼內容變更,進而觸發服務工作者更新流程。
從 Chrome 78 開始,每次為頂層服務工作者檔案執行更新檢查時,系統都會同時進行檢查,判斷是否有任何匯入的腳本內容有所變更。視所使用的 Cache-Control
標頭而定,如果 updateViaCache
設為 'all'
或 'imports'
(預設值),這些匯入的指令碼檢查可能會由 HTTP 快取完成;如果 updateViaCache
設為 'none'
,檢查可能會直接針對網路進行。
如果匯入的腳本更新檢查結果與服務工作者先前儲存的結果有位元組差異,則會觸發完整的服務工作者更新流程,即使頂層服務工作者檔案保持不變也一樣。
Chrome 78 的行為與 Firefox 在幾年前在 Firefox 56 中實作的行為一致。Safari 也已實作這項行為。
開發人員需要採取哪些行動?
如果您已有效地選擇停用 /service-worker.js
指令碼的 HTTP 快取功能,並以 Cache-Control: max-age=0
(或類似值) 放送,則不會因為新的預設行為而看到任何變更。
如果您在啟用 HTTP 快取的情況下提供 /service-worker.js
指令碼 (可能是刻意為之,或是因為這是代管環境的預設值),您可能會開始看到針對伺服器提出的 /service-worker.js
額外 HTTP 要求有所增加,這些要求原本是由 HTTP 快取完成。如果您想繼續讓 Cache-Control
標頭值影響 /service-worker.js
的新鮮度,請在註冊服務工作站時,開始明確設定 updateViaCache: 'all'
。
考量到舊版瀏覽器可能仍有大量使用者,建議您繼續在服務工作者指令碼中設定 Cache-Control: max-age=0
HTTP 標頭,即使新版瀏覽器可能會忽略這些標頭也一樣。
開發人員可以利用這個機會,決定是否要明確選擇將匯入的腳本排除在 HTTP 快取之外,並視需要在服務工作者註冊中加入 updateViaCache: 'none'
。
提供已匯入的指令碼
自 Chrome 78 起,開發人員可能會看到更多透過 importScripts()
載入的資源傳入 HTTP 要求,因為系統現在會檢查更新。
如果您想避免這類額外的 HTTP 流量,請在提供網址中包含 semver 或雜湊的腳本時,設定長效 Cache-Control
標頭,並依賴 'imports'
的預設 updateViaCache
行為。
或者,如果您想讓系統檢查匯入的腳本是否經常更新,請務必使用 Cache-Control: max-age=0
或 updateViaCache: 'none'
提供這些腳本。
延伸閱讀
建議所有將任何內容部署到網路的開發人員閱讀 Jake Archibald 撰寫的「Service Worker 生命週期」和「快取最佳做法與最大年齡陷阱」。