Ivan's Blog
主题

Progressive web app

Progressive web app 在这套 playbook 里不是“网页顺手带个 manifest”的统称,而是“把一个 Web surface 当成可安装应用去维护”的工程模式。

  • 前端

目录

Definition

Progressive web app 在这套 playbook 里不是“网页顺手带个 manifest”的统称,而是“把一个 Web surface 当成可安装应用去维护”的工程模式。它的最低门槛是 install metadata 已经被显式维护,例如 manifest、theme-color、icons、apple-touch-icon、display、scope 和真实入口路由之间的关系;更高成熟度则继续把 service worker、offline app shell、更新时机、安装后图标刷新和 owner-facing handoff 也纳入 runtime contract。图标的角色、几何、安全区、平台派生和 visual review 由 Application icon 专门定义;本 Topic 只保留它们怎样接入 PWA runtime 的规则。

为了避免把所有“有一点浏览器安装痕迹”的项目混成一类,这个 Topic 默认分三层理解:

  • metadata-only:仓库已经显式维护 site.webmanifest/manifest.webmanifest、install icons 和 standalone identity,但没有充分证据说明它已经承诺 offline/update lifecycle。KaisouMail 与 Codex Vibe Monitor 属于这一层(projects/kaisoumail.md、projects/codex-vibe-monitor.md)。
  • installable-runtime:install metadata 和真实产品入口、真实 base path、真实 owner-facing route 绑定在一起,安装面已被当成正式交付面维护。IsolaPurr USB Hub 是这一层的强样本(projects/isolappurr-usb-hub.md)。
  • offline-capable:除了安装,还把离线 app shell、service worker 更新策略、PWA 专项测试和用户可感知的更新行为做成明确 contract。PastePreset 与 IsolaPurr USB Hub 给出了最完整的现成证据(projects/paste-preset.md、projects/isolappurr-usb-hub.md)。

反过来说,单独存在 apple-touch-icon、单独注册一个给 Web Push 用的 service worker、或者只是把 display 保持为 browser,都不应该自动算作这个 Topic 命中。那类证据最多说明仓库“碰到了浏览器安装/推送边界”,但还没有形成稳定可复用的 PWA 交付习惯。

When to use

当项目满足以下任一事实时,应考虑命中这个 Topic:

  • 仓库显式维护 manifest、theme-color、install icons、apple-touch-icon,并把它们挂在真实产品入口上,而不是只给 docs-site 或静态宣传页凑元数据(projects/kaisoumail.md、projects/codex-vibe-monitor.md、projects/blog-26.md)。
  • owner-facing Web surface 被设计成“可安装的正式产品面”,安装后会以 standalone window 或 home-screen icon 的形式继续被使用(projects/isolappurr-usb-hub.md、projects/paste-preset.md)。
  • 离线 app shell、更新切换、service worker 生命周期、install icon 刷新,会直接影响真实使用体验或售后排障(projects/isolappurr-usb-hub.md、projects/paste-preset.md)。

如果仓库只有普通 SPA、Storybook、docs-site,或者只有推送通知/后台缓存这类 service worker plumbing,但没有把安装面当成正式交付面维护,就不该硬贴这个 Topic。PWA 在这套 playbook 里强调的是交付形态与维护边界,而不是浏览器特性的存在本身。

Habits / defaults

  • 先声明成熟度,不要一上来就笼统说“支持 PWA”。默认至少要分清自己现在是 metadata-only、installable-runtime 还是 offline-capable。KaisouMail 与 Codex Vibe Monitor 证明 install metadata 可以先独立成立;PastePreset 证明离线 contract 需要额外文档、构建和测试面(projects/kaisoumail.md、projects/codex-vibe-monitor.md、projects/paste-preset.md)。
  • manifest 必须跟真实产品入口绑在一起维护。start_url、scope、display、icons、theme-color 不能孤零零存在于某个静态目录里,而应与真实 route、真实 base URL、真实部署入口共同演进。IsolaPurr USB Hub 把这件事做成了正式运行 contract(projects/isolappurr-usb-hub.md)。
  • install icon 与 apple-touch-icon 属于 runtime contract,不是“品牌资源整理完再说”。一旦主人会从 Chrome/macOS/iOS 的安装面看到它们,就要把生成、命名、缓存刷新与 URL 稳定性当成正式维护对象。KaisouMail 用品牌脚本统一生成 install assets,IsolaPurr USB Hub 进一步把 icon URL 变更当成缓存刷新策略的一部分(projects/kaisoumail.md、projects/isolappurr-usb-hub.md)。
  • 图标资源要映射到 runtime,但不在这里重新发明视觉规则:manifest 的 any/maskable 用途、HTML 的 Apple touch/fav 链接、shortcut icons 和 Workbox/precache 应共同引用当前一代资产;regular、maskable、Apple 的几何和系统外壳边界一律回到 Application icon 审查。
  • 只有当离线/更新真的进入产品承诺时,才把 service worker 升级成正式 contract;一旦升级,就必须补 PWA 专项测试面。PastePreset 的 playwright.pwa.config.ts 与 tests/e2e-pwa/ 是很值得复用的默认形状(projects/paste-preset.md)。
  • 不要把“能安装”误说成“能离线正常工作”。metadata-only 项目应明确告诉维护者:现在只是 install identity 已就位,还没有证明 offline shell、waiting update、版本切换或缓存完整性。KaisouMail 与 Codex Vibe Monitor 正适合承担这类低成熟度样本(projects/kaisoumail.md、projects/codex-vibe-monitor.md)。
  • 当 manifest 已经存在但资源还不完整时,要把它当成风险而不是“迟早会补”的小尾巴。Ivan's Blog 2026 显式挂了 site.webmanifest,却仍保留空 icons 数组,这是典型的半成品 install metadata(projects/blog-26.md)。

Reference implementations

  • projects/isolappurr-usb-hub.md:最强的“PWA 是正式产品面”样本。它不只维护 manifest,而是把 app shell、真实 route、Pages/base URL、更新提示和 install icon 刷新都视为 runtime contract。
  • projects/paste-preset.md:最强的“纯前端产品也要把离线和更新说清楚”样本。它有独立 PWA 设计文档、Workbox 构建链、service worker 注册逻辑,以及单独的 PWA Playwright 测试。
  • projects/kaisoumail.md:很适合作为 metadata-only 参考。manifest、maskable icons、apple-touch-icon 和 standalone identity 都已经存在,但没有抢先声称自己已具备完整 offline/update contract。
  • projects/codex-vibe-monitor.md:适合作为“先把 install surface 与本地图标资产收口,再决定是否继续做 offline/runtime 承诺”的样本。它把 manifest、theme color 和 icon bundle 明确并入前端资产边界。

这些实现合起来说明:PWA 不是单一开关,而是一条成熟度递进路径。playbook 需要记录的不只是“有没有 manifest”,更是“这个仓库现在承诺到了哪一层,下一层要补什么门槛”。

Failure modes

  • 把 manifest 当成 checkbox,而不是 runtime contract。结果往往是入口页面挂了 rel="manifest",但 icons、scope、真实 route、base URL、缓存刷新没有一起收口。Ivan's Blog 2026 的空 icons manifest 就是现成提醒(projects/blog-26.md)。
  • 把 install metadata 做全了,就默认对外宣称“支持离线”。如果没有 service worker、offline shell、更新提示或 PWA 专项测试,这种说法只会误导维护者和用户。KaisouMail 与 Codex Vibe Monitor 的当前形态更适合被描述为“standalone install surface 已就位”,而不是完整 offline PWA(projects/kaisoumail.md、projects/codex-vibe-monitor.md)。
  • 把 service worker 只当技术细节,不定义更新策略。没有 next-launch、waiting state、刷新时机和 owner-visible UX 的约束,安装后的产品面会在版本切换时表现得非常随机。PastePreset 明确把这条链路写成了设计 contract(projects/paste-preset.md)。
  • 只替换图标文件内容,不把安装面缓存当回事。对于已安装 Web app,这会造成浏览器标签页看起来更新了,安装窗口却长期显示旧 icon。IsolaPurr USB Hub 的经验说明 install icon URL 与内容版本应一起管理(projects/isolappurr-usb-hub.md)。

Evidence index

  • projects/isolappurr-usb-hub.md
  • projects/paste-preset.md
  • projects/kaisoumail.md
  • projects/codex-vibe-monitor.md
  • projects/blog-26.md
  • skills/style-playbook/references/data/topics/progressive-web-app.json
  • skills/style-progressive-web-app/SKILL.md
  • skills/style-playbook/references/topics/application-icon/TOPIC.md