根據預設,根據服務 Worker 生命週期的規定,找到並安裝更新後的 Service Worker 時,所有目前服務工作站所控制的開啟分頁都必須關閉或進行導覽,待更新後的 Service Worker 才能啟用並接管。
在許多情況下,您可以允許在不合適的情況下進行這項作業,但在某些情況下,建議您告知使用者有待處理的 Service Worker 更新,然後自動處理切換至新 Service Worker 的程序。因此,您必須在網頁和服務工作人員中加入一些程式碼。
要加進網頁的程式碼
下列程式碼是透過 workbox-window
代管版本匯入的 JavaScript 模組,在內嵌的 <script>
元素中執行。這個方法會使用 workbox-window
註冊 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 的程式碼
取得網頁中上一節中的程式碼後,您需要將一些程式碼加進服務工作處理程序,讓系統知道何時要略過等待階段。如果您是從 workbox-build
使用 generateSW
,而且您將 skipWaiting
選項設為 false
(預設值),就可以直接使用,因為以下程式碼會自動加到產生的 Service Worker 檔案中。
如果您要編寫自己的 Service Worker (也許搭配 injectManifest
模式的其中一項 Workbox 建構工具),就需要自行新增下列程式碼:
addEventListener('message', (event) => {
if (event.data && event.data.type === 'SKIP_WAITING') {
self.skipWaiting();
}
});
這會監聽 type
值為 SKIP_WAITING
的 workbox-window
傳送至 Service Worker 的訊息,如果發生這種情況,系統會呼叫 self.skipWaiting()
。如上述程式碼範例所示,workbox-window
中的 messageSkipWaiting()
方法負責傳送這則訊息。
是否需要顯示提示?
這並不是每個部署 Service Worker 需要遵循的模式。當某些情況無法在 Service Worker 更新中重新載入網頁時,有可能會導致未預期的行為。不論是否應顯示重新載入提示,並沒有硬性和快速的規則,但以下幾種情況都適用:
- 廣泛使用預先快取。就靜態資產而言,如果針對導覽要求使用網路優先或純網路策略、延遲載入靜態資產,日後可能會發生問題。這可能會導致版本化資產可能變更,且 Service Worker 尚未預先快取。在這裡提供重新載入按鈕,可避免發生非預期的行為。
- 如果您想放送友善載入 HTML在這種情況下,請「務必」在 Service Worker 更新中提供重新載入按鈕,因為要等到更新後的 Service Worker 進行控制權後,系統才會辨識對 HTML 的更新。
- 如果不依賴執行階段快取在執行階段快取資源時,不需要讓使用者知道應重新載入。隨著版本化資產的變更,會於日後加入執行階段快取中,並假設瀏覽要求使用網路優先或純網路策略。
- 採用過時的重新驗證策略時,您可以考慮使用
workbox-broadcast-update
模組通知使用者有關 Service Worker 的更新資訊。
是否需要通知使用者更新服務工作處理程序,這取決於您的應用程式及其獨特需求。推送新的 Service Worker 時,如果您發現使用者遇到異常行為,那麼這可能就是您應通知他們的最佳信號。