Skip to content

Latest commit

 

History

History
523 lines (361 loc) · 21.3 KB

PWA_GUIDE.md

File metadata and controls

523 lines (361 loc) · 21.3 KB

📱 PWA 功能指南

📋 目录


什么是 PWA

PWA (Progressive Web App) 是一种结合网页和原生应用优点的应用技术。

PWA 优势

特性 说明
📥 可安装 添加到主屏幕,像原生应用一样启动
⚡ 资源复用 Service Worker 缓存部分资源
🔄 离线 基础功能可离线访问
🔄 自动更新 无需应用商店,自动更新
💾 小体积 无需下载大型安装包

WebAPK 支持 (Android)

在支持 WebAPK 且满足安装条件的 Android 浏览器中,安装后可以获得以下体验;实际安装形式由浏览器和系统决定:

  • ✅ 出现在应用抽屉中
  • ✅ 可在系统设置中管理
  • ✅ 独立窗口启动,隐藏常规浏览器界面

功能特性

✅ 已实现功能

1. Web App Manifest (/manifest.json)

  • 应用名称、图标、主题色
  • 独立显示模式(无浏览器UI)
  • 快捷方式(快速添加密钥、扫描二维码)
  • 协议处理器声明(web+otpauth,是否注册取决于浏览器支持)

2. Service Worker (/sw.js)

  • 静态资源缓存: 主页面、Manifest 和应用图标
  • CDN 资源缓存: jsQR、qrcode-generator
  • 智能缓存策略: 主页网络优先、Favicon 缓存优先、CDN 缓存后后台更新
  • 自动更新: 检测新版本并自动应用
  • 清理旧缓存: 保持存储空间整洁
  • 📴 离线数据修改: 支持离线添加、修改、删除和批量导入密钥
  • 🔄 离线同步: 支持 Background Sync API,并在页面加载、网络恢复或 Service Worker 控制器变化时尝试同步;不支持后台同步时由打开的页面触发

3. PWA 体验优化

  • 📱 安装入口: “设置 → 偏好设置 → 安装到桌面”,浏览器触发安装事件后启用按钮,不自动显示安装横幅
  • 📡 离线状态指示器: 实时显示在线/离线状态,离线时显示醒目的状态标识
  • 🔄 状态同步反馈: 离线操作和同步状态实时反馈给用户

4. PWA Meta 标签

  • 提供移动端视口、主题色和 Apple Web App 等元信息
  • 安装方式、独立窗口和后台能力取决于浏览器及操作系统,不能仅凭 Meta 标签保证完整支持

📴 离线功能详解

支持的离线操作

  • ✅ 查看已加载的密钥列表
  • ✅ 生成 OTP 验证码(本地计算,无需网络)
  • ✅ 添加新密钥(离线时保存,网络恢复后自动同步)
  • ✅ 修改现有密钥(离线时保存,网络恢复后自动同步)
  • ✅ 删除密钥(离线时保存,网络恢复后自动同步)
  • ✅ 批量导入(离线时保存,网络恢复后自动同步)

离线操作流程

  1. 离线时的操作

    用户操作 → Service Worker 拦截
      ↓
    保存到 IndexedDB 离线队列
      ↓
    返回"操作已保存"提示(202 状态码)
      ↓
    显示"您处于离线状态,操作已保存,网络恢复后将自动同步"
    
  2. 网络恢复时的同步

    网络连接恢复 → Background Sync 或打开的页面触发同步
      ↓
    Service Worker 按时间顺序同步所有操作
      ↓
    成功:删除队列中的操作,刷新页面数据
    网络传输失败:保留队列,等待后续同步
    登录过期:保留更改并暂停,登录成功后继续,不消耗重试次数
    已无需执行(要删除的账户已不存在,或已有服务名、账户、密钥和验证码参数都相同的账户):按成功处理并移出队列
    服务器拒绝(除 408、429 外的 4xx):立即标记为失败并显示原因
    5xx、408、429 等失败:累计 5 次后标记为失败
      ↓
    显示同步结果:"已同步 X 个离线操作"
    

技术实现

组件 说明
IndexedDB 存储待同步的离线操作
Background Sync API 浏览器支持时调度后台同步
页面同步回退 页面加载、网络恢复及控制器变化时尝试同步
Service Worker 拦截 API 请求,管理离线队列
重试机制 登录失效暂停等待登录;服务器拒绝的请求(除 408、429 外的 4xx)立即停止并显示原因;5xx、408、429 累计失败 5 次后停止自动重试;网络传输失败不消耗额度
同步顺序 按操作时间戳依次发送,尚无跨设备冲突合并

注意事项

有未完成操作时,主页显示“未同步更改”。登录过期会提示登录后继续,成功登录后自动恢复队列。其他失败项可展开查看失败原因,并单独重试或取消;被服务器拒绝的新增或编辑可点击“编辑后重试”,把内容回填到账户对话框修改后重新保存,保存成功后自动移除旧的失败项。

“编辑后重试”会显示排队内容中的密钥,因此只在当前页面已确认登录时可用:本页从服务器读到了账户列表、刚刚登录成功,或显示的是登录状态下保存的本地列表(退出登录和登录过期都会删除它)。否则页面在联网时先要求登录;离线时只提示需要联网并登录后才能编辑,不会打开无法离线完成的登录框。编辑的账户若已在别处删除,对话框会按新账户打开并说明原因,保存时新增账户,不会再编辑不存在的账户。保存修改后的内容前,页面会先占用原失败项;此时其他标签页不能重试、取消或替换它,会看到“正在其他页面中修改”的提示;若原失败项已在其他标签页同步或处理,本页不保存并说明原因,不会产生重复的更改。遇到这两种情况时,如果保存途中已关闭对话框,页面会带着修改后的内容重新打开它;此时若已打开其他账户的对话框,则只提示本次修改未保存。

取消只移除尚未同步的本地操作,不会删除服务器账户。联网后如果会话已过期,页面会清除显示的验证码并要求重新登录,离线队列保留,登录后自动继续同步。没有未同步操作时,此入口自动隐藏。

⚠️ 离线限制:

  • 离线时无法获取最新的服务器数据;页面可使用已加载或本地缓存的列表
  • 离线修改需由已激活并控制页面的 Service Worker 接收;首次登录、云端备份及恢复仍需要网络
  • 如果多设备同时离线修改同一密钥,后同步的操作会覆盖先同步的
  • 排队的新增若遇到服务名、账户和密钥都相同的已有账户:类型、位数和算法也相同(TOTP 还要周期相同,HOTP 不比较计数器和周期)时按已完成处理;参数不同时标记为失败,说明服务器上已有同名同密钥但参数不同的账户,可“编辑后重试”或取消。旧版服务器不比较参数,这种情况一律按失败处理并显示服务器原因
  • 建议单设备使用,或避免在离线时修改相同的密钥

✅ 数据安全:

  • 离线操作保存到浏览器 IndexedDB,正常关闭页面后仍可保留;清除站点数据或浏览器回收存储会删除这些记录
  • 同步需要网络和有效登录会话;请确认同步成功后再清理站点数据
  • 不支持 Background Sync 时,需要保持页面打开或联网后重新打开页面,才能触发同步

浏览器能力与回退

离线功能依赖 Service Worker 和 IndexedDB。应用会检测 Background Sync 是否可用;注册失败或不支持时,页面会发送 SYNC_OPERATIONS 消息请求同步。打开的页面在网络恢复时也会尝试同步,并非必须手动刷新。浏览器关闭后的同步时机由浏览器决定。


安装指南

📱 Android

Chrome / Edge(推荐)

  1. 访问应用

    https://your-worker.your-subdomain.workers.dev/
    
  2. 安装方式:

    将示例地址替换为自己的 Workers 地址或已配置的自定义域名。

    方式 1: 应用设置

    • 打开“设置 → 偏好设置”
    • 点击“安装到桌面”,按浏览器提示完成安装
    • 按钮置灰表示浏览器尚未触发安装事件,可尝试浏览器菜单安装
    • 在已安装的 PWA 窗口中,此设置项会隐藏

    方式 2: 浏览器原生提示

    • 浏览器地址栏可能会显示安装图标
    • 点击「添加」或「安装」

    方式 3: 手动安装

    • 点击浏览器菜单(右上角 ⋮)
    • 选择「添加到主屏幕」或「安装应用」
    • 点击「安装」
  3. 验证安装

    • 检查主屏幕或应用列表中的图标
    • 从图标启动,检查是否以独立窗口运行

特别说明: 浏览器可能安装为 WebAPK 或创建主屏幕快捷方式,具体取决于平台和安装条件。


🍎 iOS / iPadOS

Safari

  1. 访问应用

    https://your-worker.your-subdomain.workers.dev/
    
  2. 安装步骤:

    • 使用自己的部署地址,点击浏览器的 分享按钮 (↑)
    • 向下滚动,找到「添加到主屏幕」
    • 点击「添加」
    • 自定义应用名称(可选)
  3. 启动应用

    • 从主屏幕点击图标启动
    • 应用按系统支持以独立窗口运行

注意: iOS 的 PWA 支持有限:

  • 离线功能依赖已缓存的页面、数据和系统允许的站点存储
  • 不支持 Background Sync 时,由打开的页面在网络恢复后触发同步

💻 桌面平台(Windows / macOS / Linux)

Chrome / Edge / Opera

  1. 访问应用

    https://your-worker.your-subdomain.workers.dev/
    
  2. 安装方式:

    使用自己的部署地址;也可通过“设置 → 偏好设置 → 安装到桌面”发起安装(按钮可用时)。

    方式 1: 地址栏安装图标

    • 满足浏览器安装条件时,地址栏可能出现 安装图标 (⊕ 或 💻)
    • 点击图标
    • 点击「安装」

    方式 2: 菜单安装

    • 点击浏览器菜单(右上角 ⋮)
    • 选择「安装 2FA ...」
    • 点击「安装」
  3. 验证安装

    • 应用图标出现在桌面
    • 应用出现在开始菜单(Windows)或应用程序文件夹(macOS)
    • 可以独立窗口运行,无浏览器UI

Service Worker

缓存策略

当前 fetch 处理按以下规则执行:

资源 策略 网络异常时的行为
主页面 / Network First;完整成功响应更新缓存;有缓存最多等待 1.5 秒,无缓存最多等待 8 秒 返回匹配的页面缓存;无缓存时返回 503 离线提示页;明确的 401/403 不回退缓存
Favicon 代理 /api/favicon/ Cache First;成功响应写入缓存 未命中缓存且请求失败时返回空 404
白名单 CDN 库(jsQR、qrcode-generator) Stale While Revalidate;命中缓存立即返回并在后台更新 有缓存时继续使用;无缓存且网络失败时加载失败
其他 /api/ 请求 网络请求,不缓存响应 受支持的密钥写操作可进入离线队列,成功入队返回 202;GET 和不受支持的写操作返回 503
其他外部资源 Network Only;直接透传,不缓存 返回空 404
其他同源资源 Network Only;不读取 Service Worker 缓存 返回 503 文本响应

安装阶段会尝试预缓存主页、Manifest 和应用图标;Manifest 和图标的后续请求目前仍走“其他同源资源”分支,没有 Service Worker 缓存回退。API 的离线队列只在网络请求抛出错误时启用,服务器返回的 HTTP 错误会直接传回页面。

自动更新机制

页面会注册 Service Worker,并在打开期间每小时检查更新:

  • 浏览器发现新版后下载并安装;skipWaiting() 和 clients.claim() 让新版尽快接管页面
  • 已打开页面中的 HTML 和脚本不会因此自动重新加载,需要访问或刷新页面才能加载新的界面代码
  • Service Worker 更新仅记录控制台日志,不弹出更新通知

离线状态指示器:实时显示网络状态:

  • 📡 当网络断开时,页面顶部会显示"离线模式"横幅
  • 🌐 网络恢复时,指示器自动消失,并显示"正在同步..."提示
  • 离线期间的操作会自动保存到本地队列
  • 网络恢复后自动同步所有离线操作
// Service Worker 生命周期

1. 安装 (Install)
   ↓
   预缓存主页、Manifest 和应用图标
   ↓
   skipWaiting() - 立即激活

2. 激活 (Activate)
   ↓
   清理旧版本缓存
   ↓
   clients.claim() - 立即控制页面

3. 拦截请求 (Fetch)
   ↓
   根据资源类型应用不同缓存策略
   ↓
   返回缓存或网络响应

4. 更新检查 (每小时)
   ↓
   检测新版本 Service Worker
   ↓
   安装新控制器并记录日志,不自动刷新当前页面

Service Worker 版本

Service Worker 版本由部署脚本自动注入。缓存名采用 2fa-cache-${SW_VERSION},每次发布会生成对应的新缓存并在激活阶段清理旧版本,无需手工维护固定的 v1 / v2 缓存名。

查看 Service Worker 状态:

  1. Chrome DevTools:

    • F12 → Application → Service Workers
    • 查看注册状态和缓存内容
  2. chrome://serviceworker-internals/:

    • 查看所有已注册的 Service Worker
    • 可以手动卸载或更新

故障排查

问题 1: 安装按钮不可用

原因:

  • 未收到浏览器的 beforeinstallprompt 事件(应用不会自动显示安装横幅)
  • Service Worker 未成功注册
  • 未满足 PWA 安装条件
  • 浏览器不支持 PWA

解决方案:

  1. 检查 HTTPS:

    确保使用 HTTPS 访问(Cloudflare Workers 自动提供)
    
  2. 检查 Service Worker:

    • Chrome: chrome://serviceworker-internals/
    • 查找自己的部署域名,或在 DevTools → Application → Service Workers 查看当前站点
    • 确认已注册并激活;Service Worker 空闲时停止运行是正常行为
  3. 清除缓存并重新访问:

    Chrome → 设置 → 隐私和安全 → 清除浏览数据
    选择「缓存的图片和文件」→ 清除
    
  4. 检查浏览器安装入口:

    • 等待 Service Worker 完成注册;没有固定的 30 秒安装触发条件
    • 若设置中的按钮持续置灰,尝试浏览器菜单中的安装或“添加到主屏幕”选项

问题 2: Service Worker 未注册

检查方法:

  1. 打开浏览器控制台(F12)
  2. 查看是否有错误信息
  3. 检查 Application → Service Workers

常见错误:

错误 1: Failed to register service worker

原因: Service Worker 文件无法访问
解决: 确认 /sw.js 可以正常访问

错误 2: DOMException: Only secure origins are allowed

原因: 未使用 HTTPS
解决: 使用 HTTPS 访问(Cloudflare Workers 自动提供)

问题 3: 离线功能不工作

检查清单:

  1. ✅ Service Worker 已注册并激活
  2. ✅ 至少访问过一次在线版本(预缓存资源)
  3. ✅ 已加载密钥列表或存在本地密钥缓存;需要的二维码库也已在线加载并缓存

可用的离线功能:

  • ✅ 主页面UI
  • ✅ 查看已保存的密钥
  • ✅ 生成 OTP(基于本地时间)
  • ✅ 二维码生成(CDN 库已缓存)
  • ✅ 添加、编辑、删除及批量导入的请求排队(Service Worker 已控制页面,成功保存到 IndexedDB 后返回 202)

需要网络的功能:

  • ❌ 将排队的密钥修改提交到服务器、获取最新密钥列表
  • ❌ 云端备份和恢复(需要 API)
  • ❌ 登录认证(需要 API)

问题 4: 应用未更新到最新版本

手动触发更新:

  1. 浏览器刷新:

    • 按 Ctrl+Shift+R (Windows/Linux)
    • 按 Cmd+Shift+R (macOS)
  2. 卸载并重新安装:

    • Android: 设置 → 应用 → 2FA → 卸载
    • iOS: 长按图标 → 删除
    • 桌面: 右键图标 → 卸载
    • 重新访问网站并安装
  3. 清除 Service Worker:

    chrome://serviceworker-internals/
    找到自己的部署域名
    点击「Unregister」
    刷新页面重新注册
    

问题 5: CDN 资源加载失败

现象:

  • 二维码扫描功能不可用
  • 二维码生成功能不可用
  • 控制台显示 CDN 资源加载错误

解决方案:

  1. 检查网络连接:

    • 确保可以访问外部 CDN
  2. 清除缓存:

    Chrome → F12 → Application → Cache Storage
    只删除目标资源缓存,然后在线重新加载对应功能
    

    不要把清除缓存与清除全部站点数据混为一谈;后者还会删除尚未同步的 IndexedDB 操作。

  3. 检查 Service Worker 缓存:

    Chrome → F12 → Application → Cache Storage
    查看当前 `2fa-cache-*` 缓存
    确认 jsQR.min.js 和 qrcode.min.js 已缓存
    

PWA 最佳实践

用户体验

  1. 定期访问在线版本:

    • 确保获得最新功能和安全更新
    • 更新缓存资源
  2. 及时同步数据:

    • 离线数据变更会先进入本地队列,仍需在网络恢复后完成同步
  3. 保持应用更新:

    • 部署新版后在线刷新页面,加载最新界面代码;Service Worker 更新不会弹出通知

性能验证

加载耗时受设备、网络、缓存状态和密钥数量影响,没有统一的时延保证。主页在线时使用 Network First,后续访问也会请求网络。可使用浏览器 DevTools 分别记录首次访问、已有缓存的在线访问及离线访问,比较实际耗时和资源命中情况。


技术规格

Service Worker API 版本

API 项目实现状态
Cache API 已实现页面及指定资源缓存
Fetch API 已实现请求分流、离线回退和写入排队
IndexedDB 已实现持久化离线操作队列
Background Sync 已实现 sync-operations,含页面触发回退
Push Notifications 仅有接收和点击处理器,未提供完整订阅与发送流程
Periodic Background Sync 未实现

浏览器支持

项目通过能力检测使用 Service Worker、安装事件和 Background Sync。上表描述代码中已实现的功能,并非对所有浏览器的兼容性承诺。请在目标浏览器验证安装入口、离线缓存和恢复联网后的同步;HTTPS(或本地开发的可信来源)是 Service Worker 的前提。


未来计划

尚未实现的扩展

  • 周期性后台同步(当前每小时检查 Service Worker 更新依赖页面保持运行)
  • 跨设备离线修改的自动冲突检测与合并
  • 完整的推送通知订阅与发送流程

离线操作队列、密钥增删改及批量导入请求的排队、Background Sync 和页面同步回退已经实现,不属于未来功能。服务端定时备份与浏览器周期性后台同步是不同机制。


相关文档


Service Worker 使用部署时版本号自动管理缓存。排障时以 /sw.js 中的 SW_VERSION 和 Chrome DevTools 显示的当前控制器为准。