Progressive Web App(PWA)
完整指南
Service Worker、Web App Manifest、Push Notification、Offline First — 讓網頁擁有原生 App 的體驗
Section 1:什麼是 PWA,為什麼要學?
Progressive Web App(PWA)是一套讓網頁具備原生 App 體驗的技術標準,由 Google 在 2016 年提出。它不是一個框架,而是一系列 Web 標準的組合:Service Worker、Web App Manifest、Cache API、Push API 等。核心概念是「漸進增強」——在支援的瀏覽器中提供完整體驗,在不支援的環境中仍能正常運作。
Reliable(可靠)
離線可用。透過 Service Worker + Cache API,即使在網路不穩或完全離線的情況下,核心功能仍能運作。用戶不會看到瀏覽器的「無法連線」恐龍頁面。
Fast(快速)
快速載入。預先快取關鍵資源(CSS、字體、核心 JS),讓回訪者幾乎瞬間看到頁面。即使在 3G 網路下,體驗也遠優於傳統網頁。
Engaging(吸引人)
可安裝到主畫面 + Push Notification。用戶可以像使用 App 一樣使用你的網站,沒有瀏覽器的網址列干擾,還能接收即時推播通知。
Web vs Native App 的差距正在快速縮小
PWA 概念提出,Service Worker 規範在 Chrome 首先實作
iOS 11.3 開始支援 Service Worker,PWA 終於可以跨平台
Web Share API、File System Access API 讓網頁能做更多 Native 才能做的事
Web Push API 全面支援(包含 iOS 16.4+),通知功能補齊最後一塊拼圖
Project Fugu — Google 持續推進 Web 能力:藍牙、NFC、USB、攝影機控制等
PWA 的真實商業效益
- • Twitter Lite(PWA):工作階段時間增加 65%、推文增加 75%、跳出率降低 20%
- • Flipkart:App 安裝轉換率提升 70%,工作階段時長增加 3 倍
- • Starbucks PWA:用戶訂單數量是 Native App 的 2 倍
Section 2:Web App Manifest — 讓網頁可安裝
Web App Manifest 是一個 JSON 檔案,告訴瀏覽器你的網頁應該如何以「App 模式」呈現——包含名稱、圖示、啟動畫面顏色、顯示模式等。有了這個檔案,用戶才能將你的網站「加入主畫面」,讓它看起來和行為都像一個真正的 App。
完整的 manifest.json
display 選項說明
- • standalone — 全螢幕,無瀏覽器 UI(最像 Native App)
- • fullscreen — 完全全螢幕,連狀態列都隱藏
- • minimal-ui — 保留最少的瀏覽器 UI(返回鍵)
- • browser — 普通瀏覽器模式(等同沒有 PWA)
purpose(icon) 選項說明
- • any — 一般用途圖示
- • maskable — Android 可適應性圖示(會被裁切成圓形)
- • monochrome — 單色圖示,用於特殊情境
- • 建議:提供 "maskable any" 以兼顧所有場景
在 Next.js 中設定 Manifest
Next.js 13+ 支援透過 app/manifest.ts 以程式碼方式產生 manifest,或者使用 next-pwa 套件自動處理整個 PWA 配置流程。
使用 next-pwa 套件(自動化方案)
開發環境(development)中建議停用 PWA(disable: true),否則 Service Worker 的快取行為會干擾開發時的即時更新,造成改了程式碼卻看不到變化的困惑。
Section 3:Service Worker 核心原理
Service Worker 是 PWA 的核心技術,本質上是一個在瀏覽器背景執行的 JavaScript Worker,獨立於主執行緒之外。它充當瀏覽器與網路之間的代理人(Proxy),能夠攔截所有網路請求、管理快取、接收推播通知,甚至在沒有網路連線時繼續運作。
Service Worker 的角色
Install 階段
第一次載入時觸發,預先快取靜態資源(CSS、字體、離線頁面)
Activate 階段
舊版 Service Worker 被新版取代時觸發,用來清除舊快取
Fetch 階段
每次網路請求都會觸發,這裡決定要用快取還是去網路取資料
Service Worker 完整生命週期實作
為什麼要 response.clone()?
Response 是一個只能讀取一次的 Stream。如果你直接把 response 存入快取再回傳,回傳的就是空的。response.clone() 複製一份給快取存,原本的 response 繼續回傳給瀏覽器。
Section 4:快取策略選擇
Service Worker 的靈活之處在於你能根據不同的資源類型選擇最適合的快取策略。沒有萬能的策略——靜態資源、API 請求、動態內容各有最佳解。選錯策略可能讓用戶看到過期資料,或是失去離線能力。
先查快取,有就直接回傳,沒有才去網路取。適合幾乎不會更新的資源,速度最快,但要注意快取失效策略,否則用戶可能永遠看到舊版資源。
先去網路,成功則更新快取並回傳最新資料;失敗(離線)才用快取。確保資料即時性,但在網路不穩時可能較慢。
立刻回傳快取(速度快),同時在背景更新快取。下次請求就能看到最新版本。這是「速度優先,允許一次稍舊」的最佳折衷方案。
完全不快取,每次都向網路請求。用於不能使用舊資料的場景:支付、轉帳、刪除資料等。如果沒有網路就讓請求失敗,配合 UI 提示用戶。
Section 5:Push Notification 實作
Web Push Notification 讓你在用戶沒有打開你的網站時,仍能傳送通知給他們——就像 Native App 一樣。這需要三個角色配合:用戶的瀏覽器、你的後端伺服器、以及瀏覽器廠商的 Push Service(如 Firebase Cloud Messaging)。
Push Notification 流程
1. 用戶在瀏覽器授權通知 → 取得 PushSubscription
2. 前端將 PushSubscription 傳給你的後端儲存
3. 後端透過 web-push 程式庫,呼叫瀏覽器廠商的 Push Server
4. Push Server 將通知推送到用戶的瀏覽器
5. 瀏覽器的 Service Worker 接收到 push 事件,顯示通知
前端:請求權限與訂閱
後端發送 Push(Next.js API Route)
VAPID 金鑰生成
VAPID(Voluntary Application Server Identification)是 Web Push 的身份驗證機制。執行 npx web-push generate-vapid-keys 一次性生成公私鑰對,公鑰放前端,私鑰只放後端環境變數。
Section 6:Offline-First 使用者體驗設計
Offline-First 不只是技術實作,更是一種設計哲學:假設網路隨時可能中斷,在設計每個功能時就考慮離線狀態下的使用者體驗。優秀的 PWA 在離線時不只顯示錯誤訊息,而是繼續讓用戶完成工作,等到上線再自動同步。
在 React 中偵測離線狀態
Background Sync — 離線儲存,上線後自動同步
Background Sync API 讓你能在網路中斷時,將用戶的操作(例如送出表單)先暫存在 IndexedDB,等到裝置重新連線後,Service Worker 會自動在背景完成這些操作——即使用戶已經關閉了頁面。
✅ 離線 UX 原則
- • 讓用戶知道目前處於離線狀態(顯示 Banner)
- • 已載入的內容繼續可以瀏覽
- • 用戶的操作不丟失(暫存 + 同步)
- • 上線後給予成功/失敗的反饋
❌ 常見錯誤
- • 離線時只顯示空白頁面或報錯
- • 用戶不知道是否操作成功
- • 快取了敏感的個人資料在裝置上
- • 沒有提供「清除本機資料」的選項
Section 7:在 Next.js 中實作 PWA — 完整 Checklist
使用 next-pwa 搭配 Workbox 是目前在 Next.js 中實作 PWA 最常見的方案。Workbox 是 Google 出的 Service Worker 工具庫,封裝了各種快取策略,讓你不需要手寫複雜的 Service Worker 邏輯。
完整 next-pwa + Workbox 配置
PWA 上線 Checklist
manifest.json 正確填寫(name、short_name、icons、display)
HTTPS 環境(Service Worker 只在安全環境運作,localhost 除外)
提供所有必要的 icon 尺寸(192×192、512×512)
maskable icon 準備完畢(Android 適應性圖示,確保圓形裁切後不會裁到重要區域)
offline.html fallback 頁面(網路完全中斷時顯示)
Lighthouse PWA 稽核分數 ≥ 90(Chrome DevTools → Lighthouse → PWA)
Service Worker 在 Chrome DevTools → Application → Service Workers 中正確顯示
測試「Airplane Mode」下的離線體驗
iOS Safari 的特殊設定(apple-touch-icon、apple-mobile-web-app-capable)
iOS 特殊配置(在 app/layout.tsx 的 head 中加入)