Kent Shen
← 所有文章

PWA 離線功能最容易踩的坑:Service Worker 的快取為什麼更新不了

  • PWA
  • Service Worker
  • 前端

我做的照片整理工具 Latent 是一個 PWA:第一次載入之後,即使沒有網路,也能開啟、分類、匯出。離線能力靠的是 Service Worker。它很強大,但也是前端開發裡最容易讓人抓狂的東西之一:你明明改了程式,重新整理之後看到的卻還是舊的。

Service Worker 在做什麼

Service Worker 是瀏覽器在背景執行的一支腳本,它可以攔截網頁發出的所有請求,決定要回傳網路上的新資料,還是之前存起來的舊資料。離線使用就是靠它把核心檔案(HTML、CSS、JavaScript、函式庫、圖示)預先存進快取。

兩種常見的快取策略

先給快取、背景更新(stale-while-revalidate):請求進來時,立刻回傳快取裡的版本,同時在背景向網路要一份新的、更新快取。使用者感覺很快,而下次開啟就會是新版。我對自己網域的檔案用這種策略。

先試網路、失敗才用快取:適合資料經常變動的內容。

選擇哪一種,取決於「看到舊內容的代價」。對一個工具型網站,舊一次沒關係,下次就是新的了。

為什麼更新不了

最常見的原因是:Service Worker 本身也被快取了,而且新的版本會先「等待」。

瀏覽器偵測到 Service Worker 腳本的內容有變,才會安裝新版本。但新版本安裝好之後,不會立刻接管,要等到所有開著這個網站的分頁都關掉,才會啟用。所以你會遇到「改了、部署了、重新整理了,還是舊的」。

我的做法:預先快取清單加上版本號

我把核心檔案列成一份 PRECACHE 清單,並用一個 VERSION 常數命名快取。規則很簡單:

只要預先快取的清單有增減,就把 VERSION 加 1。

版本號一變,Service Worker 的內容就變了,瀏覽器會安裝新版。新版啟用時,我讓它清掉所有名稱不等於目前版本的舊快取,這樣舊檔案就不會殘留。

除錯時的幾個提醒

  • 開發時打開開發者工具的「Application → Service Workers」,勾選「Update on reload」,每次重新整理都會強制更新,不用等。
  • 如果看到舊版,到同一個面板按 Unregister,把 Service Worker 整個移除,再重新載入。
  • 只有在 localhost 與 HTTPS 底下才會啟用。 直接用 file:// 開啟時,不會有 Service Worker,這也是為什麼我在說明文件裡特別提醒:雙擊 index.html 也能用,但 HEIC 解碼這類需要 fetch 的功能會被瀏覽器擋下。
  • 部分內建預覽瀏覽器不支援 Service Worker,測試離線功能請用 Chrome 或 Edge。

第三方資源怎麼辦

Google 字型這類第三方檔案,我的做法是第一次連網時存起來,離線時沒存到的字就退回系統字型。這比強迫把它們放進預先快取清單更有彈性,也不會因為某個字型檔載入失敗,就讓整個離線功能壞掉。

要不要做離線功能

不是每個網站都需要。它帶來的是額外的複雜度,以及「使用者看到舊版」這個永遠存在的風險。我只在工具型網站、而且使用情境可能沒有網路時才加(例如在山上或國外整理照片)。其他時候,普通網站就夠了。