具代表性的面試主題

前端面試:如何安全更新 Service Worker,避免舊快取與新頁面不匹配?

前端困難
OfferCC 編輯團隊發佈 更新

題幹

你的 PWA 已部署新版本,但部分使用者仍載入舊 JavaScript 和新 HTML,頁面出現 ChunkLoadError。請解釋 Service Worker 為什麼會停在 waiting,並設計安全的版本更新、快取清理、使用者提示和復原方案。

題幹與適用場景

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 相容、錯誤監控和完整復原。

公開來源

同類題目