文章目录
- 1. 认识 PWA:定义、历史与价值
- 1.1 官方定义
- 1.2 发展时间线
- 1.3 价值主张
- 1.4 什么时候应该做 PWA
- 2. 三大支柱与技术特征
- 2.1 三大支柱(Google 官方 PWA 准则)
- Capable(能力强大)
- Reliable(稳定可靠)
- Installable(可安装)
- 2.2 渐进增强哲学
- 2.3 技术特征一览
- 2.4 最低 PWA 要求
- 3. Web App Manifest 规范详解
- 3.1 概述
- 3.2 引入方式
- 3.3 核心成员
- 3.3.1 身份成员
- 3.3.2 展示成员
- 3.3.3 图标
- 3.3.4 主题色与背景色
- 3.4 高级特性
- 3.4.1 Shortcuts(应用快捷方式)
- 3.4.2 Protocol Handlers(协议处理器)
- 3.4.3 File Handlers(文件处理器)
- 3.4.4 Screenshots(截图)
- 3.5 校验与调试
- 4. Service Worker API 深入剖析
- 4.1 什么是 Service Worker
- 4.2 关键特性
- 4.3 生命周期
- 4.3.1 注册(Registration)
- 4.3.2 安装(Installation)
- 4.3.3 激活(Activation)
- 4.3.4 空闲与终止
- 4.3.5 更新流程
- 4.4 核心事件
- 4.4.1 fetch 事件
- 4.4.2 message 事件
- 4.4.3 push 事件
- 4.4.4 sync 事件
- 4.5 作用域与安全限制
- 4.6 ServiceWorkerRegistration 对象
- 5. 缓存策略与离线架构
- 5.1 Cache Storage API
- 5.2 五大基础缓存策略
- 5.2.1 Cache First(缓存优先,回退网络)
- 5.2.2 Network First(网络优先,回退缓存)
- 5.2.3 Stale While Revalidate(陈旧内容后台更新)
- 5.2.4 Cache Only(仅缓存)
- 5.2.5 Network Only(仅网络)
- 5.3 策略选择速查表
- 5.4 App Shell 模型
- 5.5 缓存管理最佳实践
- 5.5.1 版本化缓存名
- 5.5.2 缓存清理
- 5.5.3 运行时缓存上限
- 5.5.4 跨域资源注意事项
- 5.6 进阶缓存模式
- 5.6.1 Navigation Preload(导航预加载)
- 5.6.2 Range 请求
- 6. Push API 与通知
- 6.1 概述
- 6.2 推送如何工作
- 6.2.1 推送服务
- 6.2.2 订阅流程
- 6.3 实现推送通知
- 6.3.1 请求权限
- 6.3.2 订阅推送
- 6.3.3 VAPID 身份识别
- 6.3.4 接收推送消息
- 6.3.5 处理通知点击
- 6.4 通知能力
- 6.4.1 通知选项
- 6.4.2 操作按钮
- 6.5 Web Push 协议
- 6.6 最佳实践
- 7. Background Sync 与 Background Fetch
- 7.1 Background Sync API
- 7.1.1 工作原理
- 7.1.2 注册同步
- 7.1.3 处理 sync 事件
- 7.2 Periodic Background Sync
- 7.2.1 检查支持与权限
- 7.2.2 注册周期性同步
- 7.2.3 处理 periodicsync 事件
- 7.3 Background Fetch API
- 7.3.1 启动后台下载
- 7.3.2 监听进度
- 7.3.3 处理完成
- 7.4 App Badging API
- 8. 性能优化与 Core Web Vitals
- 8.1 性能是核心特性
- 8.2 三大核心指标
- 8.2.1 LCP(Largest Contentful Paint)
- 8.2.2 INP(Interaction to Next Paint)
- 8.2.3 CLS(Cumulative Layout Shift)
- 8.3 PWA 特有的性能模式
- 8.3.1 App Shell 实现秒开
- 8.3.2 流式响应(Streamed Responses)
- 8.3.3 资源提示(Resource Hints)
- 8.3.4 代码分割与懒加载
- 8.4 性能测量工具
- 8.4.1 Lighthouse
- 8.4.2 Chrome DevTools Performance 面板
- 8.4.3 Web Vitals 真实用户数据
- 8.5 进阶性能技术
- 8.5.1 Speculative Loading(推测式加载)
- 8.5.2 HTTP/3 与 QUIC
- 8.5.3 增量静态再生成(ISR)
- 9. 安全最佳实践
- 9.1 HTTPS:一切的地基
- 9.2 Service Worker 安全
- 9.2.1 脚本完整性
- 9.2.2 作用域限制
- 9.2.3 缓存投毒防护
- 9.3 内容安全策略(CSP)
- 9.4 数据存储安全
- 9.4.1 敏感数据
- 9.4.2 缓存存储
- 9.5 认证与授权
- 9.6 第三方风险
- 9.7 OWASP PWA Security Top 10
- 10. 无障碍与包容性设计
- 10.1 无障碍是核心质量
- 10.2 WCAG 2.1 四大原则(POUR)
- Perceivable(可感知)
- Operable(可操作)
- Understandable(可理解)
- Robust(健壮性)
- 10.3 PWA 特有的无障碍考量
- 安装与引导
- 离线状态沟通
- 通知
- App Shell 与导航
- 10.4 PWA 常见无障碍陷阱
- 10.5 无障碍测试
- 11. 开发工具与框架
- 11.1 Workbox:官方 Service Worker 库
- 11.1.1 核心模块
- 11.1.2 一个基础 Workbox Service Worker
- 11.1.3 Workbox 构建工具
- 11.2 框架集成
- 11.3 浏览器开发者工具
- 11.3.1 Chrome DevTools Application 面板
- 11.3.2 Edge DevTools
- 11.4 测试与验证工具
- 11.5 部署注意事项
- Service Worker 缓存头
- HTTPS 与托管
- 更新策略
- 12. 真实案例研究
- 12.1 电商
- 阿里巴巴(Alibaba)
- 全球速卖通(AliExpress)
- JD.ID(印尼)
- 12.2 媒体与娱乐
- Hulu
- Twitter Lite
- 12.3 零售与餐饮
- 星巴克(Starbucks)
- MakeMyTrip(印度领先在线旅游平台)
- 12.4 生产力与工具
- Clipchamp(在线视频编辑器)
- Arancione(Orange 波兰客户门户)
- 12.5 案例的共性结论
- 13. 浏览器兼容性矩阵
- 13.1 核心 PWA 技术支持情况
- 13.2 平台差异
- Android / Chrome OS
- Windows
- macOS
- iOS / iPadOS
- Linux
- 13.3 渐进增强策略
- 13.4 特性检测示例
- 13.5 Polyfill 与兜底
- 14. 未来方向与新兴标准
- 14.1 Web Capabilities Project(Project Fugu)
- 14.2 即将到来的 PWA 标准
- Web App Manifest v2
- Service Worker 改进
- Push API 增强
- 14.3 安装与分发的演进
- 应用商店集成
- 更好的发现机制
- 14.4 性能创新
- View Transitions API
- Speculation Rules API
- HTTP/3 及之后
- 14.5 新兴使用场景
- PWA 作为桌面应用
- 企业级 PWA
- AI 增强的 PWA
- 14.6 长期愿景
- 15. 落地路线图
- 15.1 阶段一:打基础(第 1–2 周)
- 15.2 阶段二:增强体验(第 3–4 周)
- 15.3 阶段三:高级特性(第 5–8 周)
- 15.4 阶段四:上线与迭代(持续)
- 15.5 需要跟踪的成功指标
- 15.6 需要避开的常见坑
- 16. 常见问题 FAQ
1. 认识 PWA:定义、历史与价值
1.1 官方定义
MDN Web Docs 给出的定义是:
“A progressive web app (PWA) is an app that’s built using web platform technologies, but that provides a user experience like that of a platform-specific app. Like a website, a PWA can run on multiple platforms and devices from a single codebase. Like a platform-specific app, it can be installed on the device, can operate while offline and in the background, and can integrate with the device and with other installed apps.”
—— PWA 是使用 Web 平台技术构建、但能提供类原生应用体验的应用。它像网站一样可用一套代码运行在多平台多设备;又像平台原生应用一样可安装到设备、可离线与后台运行、可与设备及其他已安装应用集成。
Google Developers 进一步补充:
“Progressive Web Apps (PWAs) are web apps built and enhanced with modern APIs to deliver enhanced capabilities, reliability, and installability while reaching anyone, anywhere, on any device, all with a single codebase.”
关键认知:PWA 不是某一项单一技术,而是一组 Web 标准、设计模式与开发实践的集合——把 Web 的"触达能力 + 开放性"与原生应用的"能力 + 体验"结合起来。
1.2 发展时间线
| 时间 | 事件 |
|---|---|
| 2013-12-17 | 「Manifest for web apps and bookmarks」首份工作草案发布,作者为 Marcos Caceres(Mozilla)、Anssi Kostiainen、Kenneth R. Christiansen(Intel)。目标是把 Web 应用的元数据集中到单一文件,支持增强书签与添加到主屏。该规范后来演进为 Web Application Manifest。 |
| 2014-11 | Chrome 39 支持在 Android 上将 Web 应用添加到主屏,是 Manifest 驱动安装的首次主流浏览器实现。 |
| 2015 | Alex Russell 与 Frances Berriman(Google)提出「Progressive Web App」一词。同年 comScore Mobile App Report 揭示了 Web 与原生应用参与度之间的鸿沟,加速了 Web 平台演进。 |
| 2016-2017 | Service Worker API 在主流浏览器落地,带来离线与后台处理能力。Chrome 52 引入「添加到主屏幕」提示,使 PWA 安装可被用户发现。 |
| 2018 至今 | PWA 从移动端扩展到桌面端,Chrome、Edge、Safari 相继支持桌面安装;File System Access、Web Share、Protocol Handlers 等新能力持续缩小与原生应用的差距。 |
1.3 价值主张
对用户:
- 零摩擦安装:无需应用商店、无需大体积下载、无需注册账号即可试用。
- 永远最新:更新在后台自动完成,无需手动升级。
- 随处可运行:一套代码运行在任何具备现代浏览器的设备上。
- 离线可用:无网络或弱网环境下依然工作。
- 存储友好:体积通常比原生应用小数个量级(例如 Starbucks PWA 为 233KB,而 iOS 原生应用为 148MB)。
对开发者与企业:
- 单一代码库:一次开发,覆盖 Web、移动端与桌面端。
- 无平台审核:没有应用商店审批流程,没有收入分成要求。
- 触达更广:Web 天然可访问,不存在安装门槛。
- 成本更低:无需分别组建 iOS、Android、桌面团队。
- 参与度更高:可安装性、推送通知与离线能力共同提升留存与转化。
1.4 什么时候应该做 PWA
- 电商平台:转化率优化与触达范围是关键。
- 内容发布商:希望提升互动与回访率。
- SaaS 与生产力工具:需要跨多种设备类型使用。
- 新兴市场:网络与存储受限。
- 已有 Web 站点的企业:希望提升体验而不必从零重建。
2. 三大支柱与技术特征
2.1 三大支柱(Google 官方 PWA 准则)
Capable(能力强大)
现代 Web API 已能支撑过去只有原生应用才有的体验:地理定位、摄像头、文件系统访问、WebGL 图形、WebAssembly、Web Bluetooth 等,让 PWA 可以承担从照片编辑到 3D 建模的任务。Service Worker、推送通知与后台同步使应用可以脱离网络连接和浏览器标签页工作。
Reliable(稳定可靠)
可靠的 PWA 加载迅速,且无论网络条件如何都能稳定工作。用户不应看到"小恐龙"或通用浏览器错误页。即使在 3G 或完全离线状态下,应用也应能借助缓存内容瞬间打开,并优雅地处理依赖网络的功能。可靠性通过 LCP(Largest Contentful Paint)与感知性能等指标衡量。
Installable(可安装)
已安装的 PWA 与原生应用并列存在于操作系统中:可从主屏、应用启动器或开始菜单启动;出现在任务切换器与搜索结果中;可注册为特定文件类型与协议的处理器。安装带来的"持久存在感"驱动重复使用。
2.2 渐进增强哲学
「Progressive」一词指的就是渐进增强的设计哲学:
“With Progressive Enhancement, you focus on making the core functionalities of your app work universally first by using the simplest technology, then enhancing the experience for supporting devices.”
落地含义:
- 核心内容与功能在所有浏览器可用;
- 布局适配任意屏幕尺寸(响应式设计);
- 高级特性仅在浏览器支持时启用;
- 现代浏览器用户获得完整的应用级体验;
- 老旧浏览器用户仍获得可用、功能完整的网站。
2.3 技术特征一览
| 特征 | 说明 |
|---|---|
| HTTPS-only | 所有 PWA 核心 API 都要求安全上下文,Service Worker、推送通知等被限制在 HTTPS 下。 |
| Manifest-driven | Web App Manifest 文件定义应用身份、外观与安装参数。 |
| Service Worker-powered | Service Worker 作为网络代理,实现离线缓存、后台处理与推送事件处理。 |
| App-like interaction | Standalone 显示模式移除浏览器 UI,提供沉浸式类原生界面。 |
| Linkable | PWA 依然是 URL,可被分享、被搜索引擎索引、通过普通 Web 导航被发现。 |
| Re-engageable | 推送通知与 Badging API 可在应用未打开时召回用户。 |
| Updateable | Service Worker 更新机制保证应用无需用户干预即可保持最新。 |
2.4 最低 PWA 要求
浏览器据以下标准判断是否向用户展示安装提示:
- Web App Manifest,至少包含:
name或short_name- 至少一个图标(推荐 192×192 与 512×512)
start_urldisplay为standalone、fullscreen或minimal-ui
- 已注册且可用的 Service Worker,至少提供:
- 离线兜底页面
- App Shell 资源的基础缓存
- 通过 HTTPS 提供(开发环境的 localhost 豁免)
- 响应式设计,适配移动端与桌面视口
3. Web App Manifest 规范详解
3.1 概述
Web Application Manifest 是 W3C 标准的 JSON 文件,用于提供 Web 应用的元数据,是 PWA 能被安装到设备主屏并表现得像原生应用的基础技术。
W3C 规范原文:
“This metadata includes, but is not limited to, the web application’s name, links to icons, as well as the preferred URL to open when a user launches the web application. The manifest also allows developers to declare a default screen orientation for their web application, as well as providing the ability to set the display mode for the application… Additionally, the manifest allows a developer to ‘scope’ a web application to a URL. This restricts the URLs to which the manifest is applied and provides a means to ‘deep link’ into a web application from other applications.”
3.2 引入方式
<linkrel="manifest"href="/manifest.webmanifest"/>推荐扩展名为.webmanifest,MIME 类型为application/manifest+json;.json也得到广泛支持。
3.3 核心成员
3.3.1 身份成员
| 成员 | 类型 | 是否必需 | 说明 |
|---|---|---|---|
name | string | 推荐 | 应用全名,用于安装提示与应用信息页。 |
short_name | string | 推荐 | 简称,用于空间受限处(如主屏图标),建议 12 个字符以内。 |
description | string | 可选 | 应用用途简述。 |
id | string | 可选 | 同源内应用的唯一标识,对多次更新间保持应用身份一致性至关重要。 |
3.3.2 展示成员
start_url:从主屏启动应用时加载的 URL,常设为"/"或"/?source=pwa"以便统计启动来源。
display:控制启动时显示的浏览器 UI,取值:
standalone:独立窗口运行,无浏览器 UI,最接近原生;fullscreen:占满全屏,无系统 UI,适合游戏;minimal-ui:显示最小化的浏览器导航控件;browser:在普通浏览器标签页中打开(默认行为)。
orientation:锁定屏幕方向,取值any、natural、landscape、landscape-primary、landscape-secondary、portrait、portrait-primary、portrait-secondary。
scope:定义被视为应用一部分的 URL 集合。超出 scope 的导航会在普通浏览器标签页打开;scope 同时决定了 Service Worker 能控制哪些 URL。
"scope":"/app/","start_url":"/app/home"3.3.3 图标
"icons":[{"src":"/icons/icon-192.png","sizes":"192x192","type":"image/png","purpose":"any maskable"},{"src":"/icons/icon-512.png","sizes":"512x512","type":"image/png","purpose":"any maskable"}]图标 purpose:
any:可在任意位置使用的标准图标;maskable:为适配不同 OS 启动器的形状(圆形、超椭圆、圆角方形等)而设计的图标,对 Android 尤为关键;monochrome:单色图标,用于通知区域或系统托盘。
最佳实践:至少提供 192×192 与 512×512 的 PNG 图标;maskable 图标需将所有关键内容保持在中央 80% 的安全区内。
3.3.4 主题色与背景色
theme_color:定义浏览器工具栏、状态栏与应用窗口边框的颜色,应与品牌主色一致。background_color:应用加载时启动画面(splash screen)的背景色,应与首屏页面背景一致,以形成无缝过渡。
3.4 高级特性
3.4.1 Shortcuts(应用快捷方式)
"shortcuts":[{"name":"New Message","short_name":"Compose","description":"Create a new message","url":"/compose","icons":[{"src":"/icons/compose.png","sizes":"96x96"}]},{"name":"Search","url":"/search","icons":[{"src":"/icons/search.png","sizes":"96x96"}]}]3.4.2 Protocol Handlers(协议处理器)
"protocol_handlers":[{"protocol":"web+mail","url":"/compose?to=%s"}]3.4.3 File Handlers(文件处理器)
"file_handlers":[{"action":"/open","accept":{"image/png":[".png"],"image/jpeg":[".jpg",".jpeg"]}}]3.4.4 Screenshots(截图)
"screenshots":[{"src":"/screenshots/home.png","sizes":"1280x720","type":"image/png","form_factor":"wide","label":"Home screen with dashboard"}]3.5 校验与调试
Chrome DevTools 的 Application 面板提供完整的 Manifest 检查能力:
- 查看所有已解析的 manifest 成员;
- 校验图标展示与 maskable 图标安全区;
- 触发安装提示用于测试;
- 检查协议处理器与快捷方式;
- 识别错误与警告,并给出修复建议。
4. Service Worker API 深入剖析
4.1 什么是 Service Worker
Service Worker 是一种特殊类型的 Web Worker,在后台独立于页面运行,充当浏览器与网络之间的可编程网络代理,让开发者能够控制网络请求的处理方式。
W3C Service Workers 规范的定义:
“The core of this specification is a worker that wakes to receive events. This provides an event destination that can be used when other destinations would be inappropriate, or no other destination exists. For example, to allow the developer to decide how a page should be fetched, an event needs to dispatch potentially before any other execution contexts exist for that origin. To react to a push message, or completion of a persistent download, the context that originally registered interest may no longer exist. In these cases, the service worker is the ideal event destination.”
4.2 关键特性
- 事件驱动:仅在需要处理事件时运行,其余时间空闲;
- 无 DOM 访问:不能直接访问文档对象模型;
- 仅 HTTPS:要求安全上下文(localhost 开发豁免);
- 同源作用域:控制其注册 scope 内的所有页面;
- 基于 Promise:所有 API 均使用 Promise 处理异步操作;
- 后台运行:页面关闭后仍可继续处理;
- 空闲即终止:浏览器管理生命周期以节省内存。
4.3 生命周期
4.3.1 注册(Registration)
if('serviceWorker'innavigator){window.addEventListener('load',()=>{navigator.serviceWorker.register('/sw.js',{scope:'/'}).then(registration=>{console.log('SW registered:',registration.scope);}).catch(error=>{console.error('SW registration failed:',error);});});}register()返回解析为ServiceWorkerRegistration对象的 Promise。scope参数定义 Service Worker 控制哪些页面,默认为脚本所在目录及其子目录。
4.3.2 安装(Installation)
constCACHE_NAME='my-app-v1';constASSETS_TO_PRECACHE=['/','/index.html','/styles/main.css','/scripts/app.js','/icons/icon-192.png'];self.addEventListener('install',event=>{event.waitUntil(caches.open(CACHE_NAME).then(cache=>cache.addAll(ASSETS_TO_PRECACHE)).then(()=>self.skipWaiting()));});event.waitUntil()将安装阶段延长到 Promise 完成为止;任一资源缓存失败,安装即失败。self.skipWaiting()强制新 Service Worker 立即激活,而不等待所有客户端关闭。
4.3.3 激活(Activation)
self.addEventListener('activate',event=>{event.waitUntil(caches.keys().then(cacheNames=>{returnPromise.all(cacheNames.filter(name=>name!==CACHE_NAME).map(name=>caches.delete(name)));}).then(()=>self.clients.claim()));});self.clients.claim()使已激活的 Service Worker 立即接管 scope 内的所有客户端,无需等待下次页面加载。
4.3.4 空闲与终止
激活后进入空闲状态,监听事件,但浏览器可随时将其终止以节省内存。事件发生时被唤醒,处理完毕后回到空闲。
4.3.5 更新流程
用户导航到站点时,浏览器会检查 Service Worker 脚本更新。哪怕只有一个字节不同,也被视为新版本。新 Service Worker 在后台安装,旧版本继续运行;直到使用旧版本的所有标签页关闭(或调用了skipWaiting()),新版本才会激活。
4.4 核心事件
4.4.1 fetch 事件
最重要的事件,拦截 Service Worker scope 内页面发出的每一个网络请求:
self.addEventListener('fetch',event=>{event.respondWith(caches.match(event.request).then(response=>{returnresponse||fetch(event.request);}));});event.respondWith()接受一个 Response 对象或解析为 Response 的 Promise,从而可以从缓存、网络提供响应,或程序化生成响应。
4.4.2 message 事件
// 页面中navigator.serviceWorker.controller.postMessage({type:'CACHE_VERSION',version:'v2'});// Service Worker 中self.addEventListener('message',event=>{if(event.data.type==='CACHE_VERSION'){console.log('Version:',event.data.version);}});4.4.3 push 事件
服务器推送消息到达时触发(详见第 6 章)。
4.4.4 sync 事件
注册后台同步后网络恢复时触发(详见第 7 章)。
4.5 作用域与安全限制
- 路径限制:Service Worker 只能控制位于其自身目录层级或之下的 URL。位于
/js/sw.js的脚本默认只能控制/js/*。 - HTTPS 要求:仅在 HTTPS 下工作,以防中间人攻击。
- 同源策略:只能拦截来自自身源的请求。
- 无同步 XHR:不能使用同步 XMLHttpRequest 或 localStorage。
Service-Worker-AllowedHTTP 响应头可覆盖默认作用域限制,允许 Service Worker 控制比其脚本路径更广的范围。
4.6 ServiceWorkerRegistration 对象
提供以下访问能力:
installing、waiting、active:各状态下的 Service Worker 引用;scope:注册的 scope URL;update():手动检查更新;