題幹與適用場景
PWA 發布後,部分使用者仍由舊 Service Worker 控制,載入舊 JavaScript 與新 HTML 的組合,出現 ChunkLoadError。請說明生命週期、快取版本、開啟分頁、skipWaiting、clientsClaim 和復原之間的關係。
這道題適合前端、Web 平台和全端職位。題目要求討論使用者體驗與部署安全,不要求綁定某個框架。錯誤率和舊快取比例是練習資料,不是固定門檻。
面試官考察點
生命週期
候選人要說清 install、waiting、activate 和控制頁面的先後關係,尤其是更新 worker 預設等待舊用戶端退出。
資產一致性
要區分 HTML、hashed JavaScript、執行時 API 和 precache,避免清理舊快取後仍有頁面引用舊資源。
採用策略
skipWaiting 可以加快接管,但可能讓同一頁面前後使用不同版本;強回答會先評估相容性和使用者提示。
復原與觀測
需要有版本識別碼、錯誤監控、快取保留期間和 no-op worker 方案,確保壞版本能停止擴散。
回答前需要釐清的問題
- HTML 和靜態資源是否帶內容 hash?
- Service Worker 指令碼 URL 和 scope 是否穩定?
- 頁面是否可能長時間開啟或有未儲存狀態?
- API 是否向後相容兩個前端版本?
- 是否允許提示使用者重新整理或安排下一次導航?
- 發布平台能否快速復原 worker 指令碼和資產清單?
30 秒回答框架
“新 worker 安裝成功後預設進入 waiting,舊 worker 仍控制已開啟頁面。我要先讓 HTML、hashed assets 和 API 具備版本相容,再決定是否在使用者確認後 skipWaiting。快取使用版本化命名或 Workbox revision,activate 只刪除已知過期快取。
用戶端監聽 updatefound 和 controllerchange,提示使用者在安全時機重新整理,並記錄 worker 版本、ChunkLoadError 和啟用耗時。發現壞版本時先停止發布或部署 no-op worker,保留舊資源,修復後再推進。”
分步驟深入解答
第一步:畫出生命週期
註冊後 worker 進入 installing,install 成功後變成 waiting;舊 worker 仍控制現有頁面。舊用戶端退出或導航後,新 worker 才會 activate 並接管。
第二步:保證資源相容
HTML 應引用帶 hash 的靜態資源,API 在遷移期間內相容舊欄位。不要只改快取名,卻讓舊頁面請求已經刪除的 chunk。
第三步:設計更新檢查
保持 worker URL 穩定,瀏覽器會按導航和註冊檢查指令碼位元組變化。長會話可以顯式呼叫 registration.update,但不要頻繁輪詢。
~~~js const registration = await navigator.serviceWorker.ready; await registration.update(); ~~~
第四步:處理等待與提示
預設等待最安全;若產品必須立即更新,先偵測未儲存狀態和頁面相容性,再透過 postMessage 讓使用者確認 skipWaiting。controllerchange 後整頁重新整理應避免循環。
第五步:清理與復原
activate 中只刪除 allowlist 之外的快取。保留上一版靜態資源和 manifest,發生錯誤時復原入口或部署 no-op worker,不要直接清空所有快取。
第六步:觀測和驗收
記錄 worker 版本、快取命中、啟用時間、controllerchange、資源 404 和 ChunkLoadError。漸進式驗證新舊頁面組合、離線啟動、多個分頁和復原路徑。
高品質示範回答
“ChunkLoadError 很可能來自舊 worker 控制的頁面與新部署資產不相容。新 worker 安裝後會 waiting,預設要等舊用戶端退出或導航,所以我不會先全域 skipWaiting。
我會使用穩定的 worker URL、帶 hash 的靜態資源和一段 API 相容期間。預快取按 revision 管理,activate 只清理明確過期的快取。用戶端發現 waiting worker 時提示使用者在沒有未儲存狀態的時機重新整理;若必須立即切換,先確認新舊頁面協定相容,再傳送 skipWaiting,並處理 controllerchange。
發布按版本漸進式,監控 worker 版本、資源 404 和 ChunkLoadError。若新版本異常,停止入口發布或部署 no-op worker,同時保留舊資源,修復後再恢復推進。驗收包括新舊分頁、離線啟動、重新整理和復原。”
常見錯誤
- 認為 install 成功就會立即控制所有頁面。
- 不加 hash,直接覆蓋舊 JavaScript。
- 無條件 skipWaiting,導致同一頁面混用新舊版本。
- activate 直接清空所有快取。
- 改名 worker 指令碼來“強制更新”,破壞穩定註冊。
- 只測試新開啟頁面,不測試長時間開啟的舊分頁。
- API 不相容卻只復原前端入口。
- 沒有版本監控和 no-op 復原路徑。
追問與應對
追問一:為什麼新 worker 會停在 waiting?
因為舊 worker 仍控制開啟的用戶端。預設生命週期會等待這些用戶端關閉或導航,避免同一頁面中途切換控制者。
追問二:skipWaiting 什麼時候合理?
只有在資源和頁面協定向後相容、沒有未儲存狀態且產品能接受立即重新整理時。否則應等待使用者確認或下一次導航。
追問三:如何避免舊快取無限成長?
使用版本或 revision allowlist,在 activate 刪除不再使用的快取;同時保留復原所需的上一版資源保留期間。
追問四:發現壞 worker 怎麼止損?
停止新入口發布,必要時部署 no-op worker 或復原穩定指令碼;保留舊資源,修復後再逐步啟用。
追問五:如何證明更新安全?
測試多個分頁、離線啟動、舊 HTML 加新資源、重新整理、更新提示、API 相容、錯誤監控和完整復原。