PWA 離線支援
透過 @vite-pwa/nuxt 實現 Progressive Web App,支援 Service Worker 自動生成、離線快取和加入桌面提示。
啟用 PWA
// nuxt.config.ts
swiftConfig: {
pwa: true,
}
Service Worker 自動生成
啟用 PWA 後,建置時自動生成 Service Worker(Workbox),各類資源快取策略如下:
- JS / CSS / 字型(woff2):建置期 precache(
skipWaiting+clientsClaim,部署新版後下次請求即生效) - 圖片:Stale-While-Revalidate(先回快取、背景拉新,30 天 / 最多 60 條)
/api請求:NetworkFirst(網路優先,10s 逾時回退快取)- 頁面 HTML:不經 Service Worker(
navigateFallback: null,始終走網路 / Nitro)
Web App Manifest
PWA 的安裝體驗(「加入桌面」)依賴 manifest.json,框架自動生成。透過 public/icons/ 目錄放置圖示檔案:
icon-192.png— Android 主畫面圖示icon-512.png— 高解析度圖示 + 啟動畫面apple-touch-icon.png— iOS 主畫面圖示
manifest 的 head 注入由 framework 負責
產生 manifest 檔案與把它 link 進 head 是兩件事。@vite-pwa/nuxt 只把 VitePwaManifest
註冊成一個可自動匯入的元件,需要 app 自己掛載才會輸出 <link rel="manifest"> —— 漏掛了,
manifest 檔案照樣產生、Service Worker 照樣註冊,但瀏覽器從來讀不到它。
framework 直接在建置期把這行注入 app.head(fw#355),applier 無需掛 <VitePwaManifest />:
<link rel="manifest" href="/manifest.webmanifest" />
<meta name="apple-mobile-web-app-title" content="{pwa.shortName}" />
href跟隨app.baseURL(子路徑部署自動拼對),也尊重頂層vite-pwa.filename覆寫- applier 若自己在
nuxt.config寫過link[rel=manifest]或該條 meta,framework 不重複注入 - dev 期預設不注入 —— @vite-pwa 在 dev 下不產 manifest 檔案,注入只會得到 404;
本機要調 PWA 時設
SWIFT_WEB_PWA_DEV=true,manifest 與該 link 一起生效
iOS「加入主畫面」的名稱從哪來
iOS 的主畫面名稱有兩個來源,優先順序與版本相關,framework 兩條都餵:
| 來源 | 誰讀它 |
|---|---|
apple-mobile-web-app-title meta | 所有 iOS 版本;已安裝的 legacy web clip 也只認它 |
manifest 的 short_name | 支援 manifest 的 iOS(需要上面那條 link 存在) |
兩者的值同源於 pwa.manifest.short_name,不會互相打架。沒有這條 meta 且 manifest 沒被 link
時,iOS 會退回整頁 <title> —— 這正是 fw#353 的症狀(預填名變成「副標題 | 站點全名」)。
framework 刻意不注入
apple-mobile-web-app-capable(已被 manifestdisplay取代、MDN 標 deprecated)與apple-mobile-web-app-status-bar-style(屬視覺決策——black-translucent會讓 內容頂到狀態列下,應由 applier 自己定)。
需要這兩條、或想讓專案自己接管主畫面名稱時,寫在 nuxt.config.ts 的 app.head.meta 即可 —— framework 見到同名 meta 就不再注入自己那條(冪等):
app: {
head: {
meta: [
// 自己接管主畫面名稱(不寫則由 framework 用 pwa.manifest.short_name 注入)
{ name: 'apple-mobile-web-app-title', content: '浪花灣' },
// ⚠️ black-translucent 會讓內容頂到狀態列下,需自行留安全區:
// 給頂部容器加 padding-top: env(safe-area-inset-top)
{ name: 'apple-mobile-web-app-status-bar-style', content: 'black-translucent' },
],
},
},
改名稱優先改 swift.config.json 的 pwa.manifest.short_name——它與 manifest short_name 同源,iOS / Android 一起變。
manifest 欄位來源一覽
| manifest 欄位 | 來源 | 說明 |
|---|---|---|
name | swift.config.json 的 project.title(經 nuxt.config 接線) | 應用全名。與站點標題同源,改標題一處即可 |
short_name | nuxt.config.ts 顶层 pwa.manifest.short_name | 主畫面圖示下方的名稱,也是 iOS 加入主畫面的預填值。預設同 title,超過約 12 字要自己改短 |
description | swift.config.json 的 project.description(經 nuxt.config 接線) | 與 SEO 描述同源 |
theme_color | nuxt.config.ts 顶层 pwa.manifest.theme_color | Android 工作卡片 / 系統 chrome 配色。UI 稿出來後改 |
background_color | nuxt.config.ts 顶层 pwa.manifest.background_color(預設 #ffffff) | 冷啟動鋪滿的那一屏底色,深色站必配,見下 |
lang | 自動取 framework.i18n.defaultLocale | 不另開配置面 |
display / start_url / icons | framework 固定 | 見上文 |
為什麼分兩處:「開不開 PWA」在
swift.config.json(sw config set framework.pwa true/false, CLI 管;不再聯動裝卸@vite-pwa/nuxt,該模組由 framework 統一攜帶);「叫什麼、什麼顏色」在nuxt.config.ts—— 建專案時你只有一個站名, 描述沒想好、UI 稿沒出,顏色問不出來;而 JSON 裡沒註解、看不見的欄位事後沒人回頭補。 starter 模板已把這些欄位逐條接好線並帶註解,改的時候看得見。
🔴 深色站必配 backgroundColor
PWA 從主畫面圖示冷啟動時,系統先按 background_color 鋪滿一屏、再畫首幀。預設值是 #ffffff,
深色站不配就會先閃一下白。
這個白閃只在真機冷啟動的一瞬出現 —— PC 與開發期幾乎撞不到,屬於「寫錯了自己看不見」那一類,
所以 framework 給了配置口子而不是讓業務自己去覆寫 manifest。取站點主背景色,改 nuxt.config.ts:
pwa: {
manifest: {
// …
background_color: '#0a1213', // 深色站主背景色
},
},
其餘 manifest 欄位:頂層 pwa 逃生艙
上表之外的欄位(categories / shortcuts / screenshots / 換 icons 等)走 nuxt.config.ts
頂層的 pwa 模組選項,與 framework 的 defaults 深合併:
pwa: {
manifest: {
categories: ['entertainment'],
shortcuts: [{ name: '我的', url: '/mine' }],
},
},
⚠️ 別用它覆寫上表裡那幾個欄位 —— 會與 framework.pwa 形成兩個事實源,改一處不生效。
專案名稱和主題色
starter 模板在 nuxt.config.ts 裡已把這些欄位逐條接好線:
// 頂層 pwa 是 @vite-pwa/nuxt 的模組選項,用它自帶的型別,無需從 framework 引入
import swiftConfig from './swift.config.json';
pwa: {
manifest: {
name: swiftConfig.project.title, // 改站點標題一處即可
short_name: swiftConfig.project.title, // 太長就在這裡改短
description: swiftConfig.project.description, // 與 SEO 同源
theme_color: '#3b82f6', // UI 稿出來後改品牌主色
background_color: '#ffffff', // 深色站必改
},
},
直接改這裡,不要寫進 swift.config.json 的 framework.pwa —— 那會變成第二處事實源,
project.title 改了它不會跟。framework.pwa 在 swift.config.json 裡只承擔布林開關。
不接線會怎樣:
framework.pwa傳布林true時,name/shortName取佔位預設App,themeColor取#3b82f6—— 裝到手機主畫面上就叫「App」。
加入主畫面引導
PWA 啟用時,framework 會自動註冊 <SwAddToHomescreenGuide> 元件,
僅需在 app.vue 掛載即可獲得"H5 使用者首次造訪時引導加入主畫面"的能力。
透過 addToHomescreen 子選項可調整行為:
// nuxt.config.ts —— 🔴 **與 `pwa` 平級的頂層欄位,不在 `pwa.manifest` 裡**(fw#359 已平級化)
swiftConfig: {
...swiftConfig.framework,
addToHomescreen: {
delay: 2000,
rememberFor: 'session',
},
},
🔴 別寫進
pwa.manifest:那是 Web App Manifest 的內容,vite-plugin-pwa會把它整份寫進manifest.webmanifest——addToHomescreen會變成清單裡的垃圾欄位,而引導行為完全不生效。 本項也不進swift.config.json(CLI 不管它)。只寫你要改的欄位即可:enabled省略時跟隨framework.pwa,delay/rememberFor各有預設值。
| 子選項 | 類型 | 預設 | 說明 |
|---|---|---|---|
addToHomescreen.enabled | boolean | 跟隨 pwa.enabled | 顯式關閉可保留 PWA 但不彈引導 |
addToHomescreen.delay | number | 2000 | 造訪後延遲多久彈(毫秒) |
addToHomescreen.rememberFor | 'session' | PeriodSpec | 'session' | 記憶策略,'session' 或 'Nm'·'Nh'·'Nd'(如 '7d') |
完全不要引導:
swiftConfig: {
...swiftConfig.framework,
addToHomescreen: false,
},
詳細 API 見 SwAddToHomescreenGuide 文檔。
開發模式注意事項
Service Worker 僅在生產建置中生效,pnpm dev 時不會註冊 SW(需要 dev 期調試 SW 時,在 .env.local 設 SWIFT_WEB_PWA_DEV=true)。測試 PWA 功能需要:
pnpm build && pnpm preview
# 然後用 Chrome 開發者工具 → Application → Service Workers 驗證