根據預設,Service Worker 生命週期會要求在找到並安裝更新的 Service Worker 時,先關閉目前 Service Worker 所操控的所有開啟分頁,或在更新前啟用導覽功能,然後才會啟用並接管更新版 Service Worker。
在許多情況下,或許可在某個時間內允許這種情況發生,但在某些情況下,您可能會想提前告知使用者有待處理的 Service Worker 更新,然後自動切換至新的 Service Worker。要做到這一點,您必須在網頁及服務工作人員中加入一些程式碼。
要放在網頁中的程式碼
下列程式碼會使用從 CDN 託管版本 workbox-window
匯入的 JavaScript 模組,在內嵌 <script>
元素中執行。它會使用 workbox-window
註冊 Service Worker,並在 Service Worker 停滯在等候階段時回應。當找到正在等待的 Service Worker 時,這段程式碼會通知使用者新版網站可供使用,並提示使用者重新載入。
<!-- This script tag uses JavaScript modules, so the proper `type` attribute value is required -->
<script type="module">
// This code sample uses features introduced in Workbox v6.
import {Workbox} from 'https://storage.googleapis.com/workbox-cdn/releases/6.4.1/workbox-window.prod.mjs';
if ('serviceWorker' in navigator) {
const wb = new Workbox('/sw.js');
let registration;
const showSkipWaitingPrompt = async (event) => {
// Assuming the user accepted the update, set up a listener
// that will reload the page as soon as the previously waiting
// service worker has taken control.
wb.addEventListener('controlling', () => {
// At this point, reloading will ensure that the current
// tab is loaded under the control of the new service worker.
// Depending on your web app, you may want to auto-save or
// persist transient state before triggering the reload.
window.location.reload();
});
// When `event.wasWaitingBeforeRegister` is true, a previously
// updated service worker is still waiting.
// You may want to customize the UI prompt accordingly.
// This code assumes your app has a promptForUpdate() method,
// which returns true if the user wants to update.
// Implementing this is app-specific; some examples are:
// https://open-ui.org/components/alert.research or
// https://open-ui.org/components/toast.research
const updateAccepted = await promptForUpdate();
if (updateAccepted) {
wb.messageSkipWaiting();
}
};
// Add an event listener to detect when the registered
// service worker has installed but is waiting to activate.
wb.addEventListener('waiting', (event) => {
showSkipWaitingPrompt(event);
});
wb.register();
}
</script>
如果接受,messageSkipWaiting()
就會指示等待服務工作站叫用 self.skipWaiting()
,表示接下來會開始啟用。啟用後,新的 Service Worker 將負責控管任何現有用戶端,並在 workbox-window
中觸發 controlling
事件。在這種情況下,系統會使用最新版所有預快取資產,以及更新版 Service Worker 中的任何新轉送邏輯,重新載入目前的網頁。
要放入 Service Worker 的程式碼
從上一節中取得程式碼後,您必須在服務 Worker 中新增一些程式碼,讓服務工作人員知道何時可以略過等待階段。如果您使用 workbox-build
的 generateSW
,且已將 skipWaiting
選項設為 false
(預設),那麼沒問題,因為產生的 Service Worker 檔案會自動包含下列程式碼。
如要編寫自己的 Service Worker (可與其中一個 Workbox 建構工具在 injectManifest
模式下使用),必須先自行新增下列程式碼:
addEventListener('message', (event) => {
if (event.data && event.data.type === 'SKIP_WAITING') {
self.skipWaiting();
}
});
這將會監聽由 workbox-window
傳送至服務工作站的訊息 (type
值為 SKIP_WAITING
),並在呼叫 self.skipWaiting()
時呼叫 self.skipWaiting()
。workbox-window
中的 messageSkipWaiting()
方法 (如先前的程式碼範例所示) 負責傳送這則訊息。
您需要顯示提示嗎?
這不是每個部署 Service Worker 必須遵循的模式。在特定情況下,如果您未能在服務工作人員更新時重新載入頁面,可能造成非預期的行為。至於是否該顯示重新載入提示,並沒有硬性規定,以下列出一些可能適合您的情況:
- 廣泛使用預先快取。對於靜態資產,如果使用以網路優先或僅限網路的策略處理導覽要求,但使用延遲載入的靜態資產,則可能會發生問題。這可能會導致版本化資產發生異動,且 Service Worker 尚未預先快取這些資產。在這裡提供重新載入按鈕,可避免發生非預期的行為。
- 您是否要提供預先快取的 HTML。在這種情況下,請「強烈」考慮在 Service Worker 更新時提供重新載入按鈕,因為只有在更新後的 Service Worker 主受更新後,系統才會辨識 HTML 的更新。
- 如果您主要不是仰賴執行階段快取,在執行階段快取資源時,您不需要告知使用者應重新載入資源。版本化資產變更之後,系統會在一段時間後將這些資產加入執行階段快取,假設導覽要求採用「以網路為優先」或「僅限網路」策略。
- 使用「過時的重新驗證」策略時,可考慮使用
workbox-broadcast-update
模組通知使用者有關 Service Worker 的更新資訊。
您是否需要通知使用者更新的服務工作處理程序,取決於您的應用程式及其獨特需求。如果您發現在推送新的 Service Worker 時使用者遇到異常行為,這可能就是您應該通知使用者的最佳信號。