3.7 PWA支持


文档摘要

3.7 PWA 支持:缓存与离线 PWA(渐进式 Web 应用)让 Web 应用获得可安装、离线可用、弱网可用的能力。Angular 通过 CLI 一条命令接入 Service Worker 与清单。主线看点:缓存改变的是"应用数据与静态资源从哪来",而 2.8 节的结论不变——响应到达仍是异步事件,检查照常被唤醒;新增的问题反而是缓存陈旧导致"界面显示了旧数据"。 本节要拿下什么 到目前为止应用都活在"网络永远畅通"的假设里,本节要拿下断网场景的兜底方案: 用 CLI 添加 PWA 支持并解释生成的各部分职责。 配置 Service Worker 的多组缓存策略(预缓存与运行时缓存)。 实现新版本检测与更新提示的完整交互。 排查"改了代码线上没变"的缓存陈旧问题。

3.7 PWA 支持:缓存与离线

PWA(渐进式 Web 应用)让 Web 应用获得可安装、离线可用、弱网可用的能力。Angular 通过 CLI 一条命令接入 Service Worker 与清单。主线看点:缓存改变的是"应用数据与静态资源从哪来",而 2.8 节的结论不变——响应到达仍是异步事件,检查照常被唤醒;新增的问题反而是缓存陈旧导致"界面显示了旧数据"。

本节要拿下什么

到目前为止应用都活在"网络永远畅通"的假设里,本节要拿下断网场景的兜底方案:

  1. 用 CLI 添加 PWA 支持并解释生成的各部分职责。
  2. 配置 Service Worker 的多组缓存策略(预缓存与运行时缓存)。
  3. 实现新版本检测与更新提示的完整交互。
  4. 排查"改了代码线上没变"的缓存陈旧问题。

一、一条命令的接入

# 为项目添加 PWA 支持 ng add @angular/pwa # 生成内容:清单文件、图标集、Service Worker 注册、ngsw 配置

生成物里最核心的是 Service Worker 配置(描述缓存策略)与应用清单(描述安装信息:名称图标启动方式)。构建生产包后,Service Worker 会接管静态资源的缓存——预缓存:构建产物在安装时全部入缓存,之后离线也能完整启动应用。

二、缓存策略配置

// Service Worker 配置文件示意 { "index": "/index.html", "assetGroups": [ { "name": "app", // 应用壳:预缓存 整组 "installMode": "prefetch", "resources": { "files": ["/*.html", "/*.css", "/*.js"] } }, { "name": "assets", // 图片字体:访问时才缓存 "installMode": "lazy", "updateMode": "prefetch", "resources": { "files": ["/assets/**"] } } ], "dataGroups": [ { "name": "api-fresh", // 接口:网络优先 短超时回退缓存 "urls": ["/api/orders/**"], "cacheConfig": { "strategy": "freshness", "maxSize": 100, "maxAge": "1h", "timeout": "3s" } }, { "name": "api-static", // 配置类接口:缓存优先 后台刷新 "urls": ["/api/dict/**"], "cacheConfig": { "strategy": "performance", "maxAge": "24h", "maxSize": 50 } } ] }

两组策略的取舍标准是数据的新鲜度要求:交易类数据网络优先(freshness,宁可慢三秒也要新的),字典类数据缓存优先(performance,先出界面后台更新)。这个分类与 2.9 节状态分类一脉相承——客户端状态、服务端数据、以及介于两者之间的"可陈旧数据",各自需要不同的一致性等级

三、新版本检测与更新提示

import { Injectable, inject } from '@angular/core'; import { SwUpdate } from '@angular/service-worker'; import { Subject } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class UpdatePromptService { private swu = inject(SwUpdate); readonly newVersionReady = new Subject<void>(); constructor() { if (this.swu.isEnabled) { // 开发模式无 worker,先判断 this.swu.versionUpdates.subscribe(evt => { if (evt.type === 'VERSION_READY') { // 新版本已下载完毕:提示用户,而不是静默等下次刷新 this.newVersionReady.next(); } }); } } applyNow() { // 用户确认后激活新版本并重载 document.location.reload(); } }

Service Worker 的版本更新机制:后台下载新版产物 → 旧 worker 继续服务当前会话 → 下次冷启动切换。versionUpdates 流把这个过程暴露给应用,团队可以选择静默等待或显式提示——面向交易的后台我倾向显式提示并让用户选时机,避免操作中途界面突然重载。

⚠️ 经典排查:"改了代码线上没变"——先怀疑 Service Worker 在提供旧的应用壳。开发验证用无痕窗口或暂时注销 worker;面向用户则依赖版本提示机制兜底。

图:一次接口请求在 PWA 层的三种命运

图:一次接口请求在 PWA 层的三种命运

四、案例:仓库巡检离线化的完整过程

背景:仓库管理员在信号差的库区用平板巡检,纯在线应用频繁白屏。要求:巡检单离线可开、可暂存、出库区后自动补传。
操作:CLI 添加 PWA;字典与商品基础数据走 performance 策略(提前缓存);巡检提交改为"本地队列 + 网络恢复监听重放":

private pending: Draft[] = []; submit(draft: Draft) { this.pending.push(draft); this.tryFlush(); } async tryFlush() { const online = navigator.onLine; if (!online) return; // 离线:留在队列 while (this.pending.length) { const d = this.pending[0]; try { await firstValueFrom(this.http.post('/api/inspect', d)); this.pending.shift(); // 成功一条弹一条 } catch { break; } // 失败:停止重试 等下次 } } constructor() { fromEvent(window, 'online').subscribe(() => this.tryFlush()); }

结果:库区内正常开单暂存,走出库区网络恢复后队列自动清空上传。
解读:离线补传把"网络可用性"从硬前提变成了排队状态,界面仍由本地状态驱动——变更检测完全无感,这正是分层清晰的回报。
变式:补传需要防重(服务器可能已收到但响应丢失),给每条草稿带客户端生成的幂等键,服务端按键去重。

本节要点回顾

  • 接入:一条命令获得清单、图标与 Service Worker,生产构建自动启用。
  • 两组策略:assetGroups 管静态资源(预缓存/惰性),dataGroups 管接口(新鲜优先/性能优先)。
  • 版本更新:后台下载、下次启动切换,versionUpdates 流支撑显式提示。
  • 缓存陈旧:先查 worker 是否在供旧壳;交易数据务必网络优先。
  • 主线上的一环:缓存改变数据来源,不改变"响应到达即检查"的因果链;离线写操作是本地状态的排队问题。

下一节:服务端渲染。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U