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会把它 整份Object.assign后写进manifest.webmanifest——addToHomescreen会变成清单里的 垃圾字段,而引导行为完全不生效(framework 根本不读那个位置)。本项也不进
swift.config.json(CLI 不管它,与auth/systemInfo同类)。只写你要改的字段即可,其余走默认:
enabled省略时跟随framework.pwa,delay/rememberFor各有默认值。
| 子选项 | 类型 | 默认 | 说明 |
|---|---|---|---|
addToHomescreen.enabled | boolean | 跟随 framework.pwa(可省略) | 显式关闭可保留 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 验证