面试官:请你实现一个 PWA,我:😭
Category(分类): JavaScript Status: 未知
配图来自原文页面,已下载并转换为本地 WebP;原始授权未核实,发布前请人工确认。
前言
渐进式 Web 应用(Progressive Web App,简称 PWA)使用 Web 平台技术构建,但可以在支持的浏览器和操作系统中提供接近独立应用的体验,例如安装到主屏幕、离线或弱网工作、推送通知和系统集成。
PWA 不是一个单独的框架,也不是一份在所有浏览器中都完全相同的功能清单。它强调渐进增强:不能安装或不支持 Service Worker 的浏览器,仍然应该能把应用当作普通网站使用;可安装性、通知、后台同步、硬件访问和展示模式都取决于浏览器、操作系统、权限和用户设置。



原文以 2020–2021 年的面试场景为背景,下面保留 22 个问题,并把已经变化的结论标注出来。
问题 1:什么是 PWA?
难度:⭐
PWA 是使用 HTML、CSS、JavaScript 和各种 Web API 构建的 Web 应用。它可以通过 URL 被发现、分享和搜索,也可以在支持的平台上安装,以独立窗口或浏览器标签页运行。
一个成熟的 PWA 往往具备以下特征:
- 可发现:内容仍然是 Web 内容,可以被链接和搜索。
- 可安装:通过 Web App Manifest 和浏览器/平台支持,显示为设备上的应用入口。
- 可离线或弱网工作:通常借助 Service Worker、Cache API 和 IndexedDB。
- 可重新参与:在获得权限且平台支持时使用 Web Push、Notifications、Badging 等能力。
- 响应式和可访问:适配不同屏幕、输入方式和辅助技术。
- 渐进增强:缺少某项 API 时仍能提供基本 Web 功能。
“像原生 App”是用户体验目标,不代表 PWA 自动拥有原生应用的全部系统权限。硬件、后台任务、推送和文件访问都要逐项检查兼容性和权限。
问题 2:PWA 有哪些优点?
难度:⭐⭐
原文提到的优点仍有参考价值,但不应把它们写成无条件保证:
- 一个代码库覆盖多个平台:减少重复开发,但仍需要针对浏览器、屏幕、输入方式和系统能力做适配。
- 可链接、可搜索、可分享:这是 Web 的优势;SEO 取决于内容可抓取、渲染方式、结构化数据和性能,并不是安装 PWA 自动带来排名提升。
- 安装摩擦较低:用户可以直接使用 URL;支持的平台还可以把它安装为应用。但安装提示由浏览器决定,不是每次访问都会出现。
- 更新灵活:网页资源可以从服务器更新,不需要等待应用商店审核;Service Worker 缓存策略设计不当时也可能导致旧资源,所以必须设计版本化和更新提示。
- 离线和弱网能力:Service Worker 可以拦截受控页面的请求,Cache API 可以保存静态资源,IndexedDB 可以保存结构化数据。
- 响应式体验:同一 Web 应用可以适配手机、平板、桌面等设备,但需要真正的响应式布局和可访问性实践。
- 安全模型清晰:Service Worker、Push 等能力通常要求 HTTPS 或
localhost安全上下文;HTTPS 是必要条件之一,不等于应用代码天然安全。 - 可以使用 Web 平台能力:通知、分享、文件、蓝牙、USB、地理位置等 API 的可用范围和权限各不相同,不能统一概括为“访问设备硬件”。
- 可选择应用商店分发:PWA 可以只通过 Web 分发,也可以使用 PWABuilder、Trusted Web Activity 等方式打包到部分应用商店;商店审核并没有被 PWA 永久绕过。
“更小更快”“零安装”“不占磁盘”都是可能的体验,不是 PWA 的必然结果。首次加载仍需要下载资源,安装后也会占用存储空间。
问题 3:PWA 有哪些缺点?
难度:⭐⭐
- 平台能力不完全一致:浏览器和操作系统对安装、推送、后台同步、文件系统、蓝牙、分享和窗口控制的支持不同。
- 后台执行受限制:Service Worker 可以被浏览器终止并在事件发生时重新启动,不能当成一个一直运行的后台进程。
- 存储不是永久保险箱:Cache Storage、IndexedDB 等数据受到配额、用户清理和存储压力回收影响;需要持久业务数据时应同步到服务器,并按需申请持久化存储。
- 调试和更新复杂:Service Worker 有独立生命周期,旧版本可能处于 waiting,缓存版本管理错误会造成白屏或旧资源混用。
- 系统集成有限且需要权限:通知、推送、后台同步和硬件 API 可能需要用户授权,在部分浏览器或平台不可用。
- 应用商店分发仍有成本:如果要进入应用商店,仍需遵守对应商店的打包、审核和隐私政策。
- 性能并非天然优于原生:复杂图形、持续后台任务、深度系统集成和大规模本地计算可能更适合原生或其他专用方案。
iOS/iPadOS 的 PWA 能力已经比早期文章发布时丰富:Service Worker、Cache API 和“添加到主屏幕”均可用,iOS/iPadOS 16.4 起主屏幕 Web App 还支持 Web Push。但具体能力仍应以目标版本和 WebKit 文档为准。
问题 4:如何让一个 Web 应用成为 PWA?
难度:⭐⭐
可以从以下方面逐项建设:
- 可发现:页面有真实 URL、清晰内容和合理的 SEO/可访问性结构。
- 可安装:提供 Web App Manifest,并满足目标浏览器的平台条件。
- 安全:通过 HTTPS 提供;本地开发通常使用
localhost或127.0.0.1。 - 可离线或弱网工作:注册 Service Worker,设计合适的缓存和回退策略。
- 渐进增强:不支持某项 API 时仍能正常浏览和完成核心任务。
- 可重新参与:在权限、浏览器和平台都支持时加入 Push、Notifications、Badging 等能力。
- 响应式和可访问:适配设备、输入方式、屏幕阅读器和键盘操作。
- 可维护:缓存有版本号,Service Worker 更新有回滚和用户提示方案。
PWA 没有一张能覆盖所有浏览器的“勾选表”。基于 Chromium 的安装提示有自己的 manifest 和 Service Worker 条件,iOS、Safari、Firefox 和桌面平台的安装流程也不同。
问题 5:为什么 PWA 需要 Web App Manifest?
难度:⭐⭐
Web App Manifest 是一个 JSON 文件,用来描述应用的名称、图标、启动地址、显示模式、主题色、作用域等信息。它是浏览器了解“如何把这个 Web 应用作为应用展示”的重要入口,但不是权限声明,也不会自动授予通知或硬件权限。
在 HTML 中引用:
<link rel="manifest" href="/manifest.webmanifest">
一个现代的最小示例:
{
"id": "/",
"name": "我的 PWA",
"short_name": "我的应用",
"start_url": "/",
"scope": "/",
"display": "standalone",
"theme_color": "#ffffff",
"background_color": "#ffffff",
"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"
}
]
}
不同浏览器的必需字段和图标要求可能不同。id 用于稳定识别应用,start_url、scope、图标 URL 和 manifest 本身应满足同源/CORS 等加载条件;manifest 不要求必须放在网站根目录。
问题 6:PWA 有哪些原生 App 不具备的特性?
难度:⭐⭐⭐
与传统原生应用相比,PWA 的 Web 特性更突出:
- URL 可直接分享,应用内的页面可以成为独立入口。
- 搜索引擎可以抓取公开内容,具体效果取决于渲染和 SEO 实现。
- 用户可以先访问再决定是否安装,不一定需要先进入应用商店。
- Web 应用可以快速发布和迭代,不必为每次内容更新提交商店审核。
- 同一套 Web 代码可以覆盖多个操作系统和设备。
- Service Worker 可以在合适的时机提供离线缓存。
这些优势也伴随限制:Web 权限模型更严格,浏览器可能拒绝安装提示、限制后台执行或回收本地存储。不能把“绕过应用商店”理解成绕过安全、隐私和分发规则。
问题 7:Service Worker 是什么?
难度:⭐⭐⭐
Service Worker 是运行在独立 Worker 上下文中的事件驱动脚本,通常用于代理受控页面的网络请求、缓存资源、处理 Push/Notification 相关事件以及部分后台任务。它:
- 没有 DOM 访问权限;
- 通常要求 HTTPS,
localhost等本地安全来源可用于开发; - 受 scope 限制,只能控制作用域内的页面和请求;
- 可能被浏览器终止,下一次事件到来时再启动;
- 通过
event.waitUntil()延长安装、激活等生命周期事件; - 不是一个一直运行的后台线程,也不能直接操纵页面 DOM。
注册 Service Worker:
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js', { scope: '/' })
.then(registration => {
console.log('注册成功', registration.scope)
})
.catch(error => {
console.error('注册失败', error)
})
}
Service Worker 的能力并不等于所有 PWA 能力。Push、Background Sync、Periodic Background Sync 等 API 需要分别检查浏览器支持和权限。
问题 8:PWA 与 Hybrid 有什么不同?
难度:⭐⭐⭐
Hybrid 通常把 Web 技术运行在原生 WebView 中,并通过 JavaScript Bridge 调用原生能力,再以原生应用包的形式通过应用商店分发。Cordova、Capacitor、Ionic 等方案都可以构建这类应用,具体架构因工具而异。
PWA 主要运行在浏览器或浏览器提供的独立窗口中,依靠 Web API 和 Service Worker,不必先安装原生应用包。PWA 也可以借助 Trusted Web Activity、PWABuilder 等工具进入部分应用商店,因此二者并不是“只能商店发布”和“绝不能商店发布”的简单二分。
简单对比:
| 方面 | PWA | Hybrid |
|---|---|---|
| 运行容器 | 浏览器或独立 Web App 窗口 | 原生 WebView/应用容器 |
| 分发方式 | URL、安装提示、可选应用商店 | 通常是应用商店安装包 |
| 原生能力 | 受浏览器权限和 API 支持限制 | 可通过 Bridge 使用更多原生 API |
| 更新方式 | Web 资源可直接更新,但要管理缓存 | 代码和原生能力受应用包版本影响 |
| 适合场景 | 可链接、跨平台、离线 Web 应用 | 需要深度系统集成或商店能力的应用 |
问题 9:什么是 fetch 事件?
难度:⭐⭐⭐
当一个页面已经被 Service Worker 控制后,作用域内符合条件的请求可以触发 Service Worker 全局的 fetch 事件。第一次注册 Service Worker 的页面通常需要重新加载,或者由 Service Worker 调用 clients.claim() 后才会被控制。
一个更安全的 cache-first 示例:
self.addEventListener('fetch', event => {
const request = event.request
// 通常只缓存 GET;POST 等请求不要直接放进 Cache API。
if (request.method !== 'GET') return
event.respondWith((async () => {
const cached = await caches.match(request)
if (cached) return cached
try {
const response = await fetch(request)
const url = new URL(request.url)
// 示例只缓存同源且成功的响应,实际项目应按资源类型设计策略。
if (response.ok && url.origin === self.location.origin) {
const cache = await caches.open('runtime-v1')
await cache.put(request, response.clone())
}
return response
} catch (error) {
const fallback = await caches.match('/offline.html')
if (fallback) return fallback
throw error
}
})())
})
缓存策略必须考虑请求方法、响应状态、同源/CORS、缓存版本和隐私数据。不能把所有请求都缓存,也不能把缓存当成服务器数据的唯一来源。
问题 10:对网站进行 PWA 安装有哪些要求?
难度:⭐⭐⭐
没有一份适用于所有浏览器的统一安装清单。常见的 Chromium Web 安装条件包括:
- 通过 HTTPS 提供,或使用
localhost等安全开发来源; - HTML 正确引用 Web App Manifest;
- manifest 包含目标浏览器要求的名称、图标、
start_url、display等字段; - 注册 Service Worker,并提供能支持基本离线体验的
fetch事件处理; - 应用满足浏览器的用户体验、内容和平台要求。
在 iOS/iPadOS 上,用户可以通过分享菜单将网站添加到主屏幕,流程和 Chromium 的安装提示不同。安装提示也可能因用户已经安装、浏览器策略、设备平台或应用状态而不出现。
因此工程上应使用浏览器开发者工具、Lighthouse 和目标设备实测,不能只检查一个 manifest 文件就断言“已经是可安装 PWA”。
问题 11:什么是 IndexedDB,如何在 PWA 中使用?
难度:⭐⭐⭐
IndexedDB 是浏览器提供的异步、事务性、面向对象的客户端数据库,适合存储较大量的结构化数据、索引和 Blob。它与 Cache API 的职责不同:
- 静态资源和 Request/Response:通常使用 Cache API;
- 业务数据、离线队列、结构化对象:通常使用 IndexedDB;
- 需要跨设备或不可丢失的数据:仍应同步到服务器。
IndexedDB API 较底层,实际项目可以使用 Dexie 等库简化事务和版本迁移,但要评估依赖和兼容性。
const request = indexedDB.open('pwa-demo', 1)
request.onupgradeneeded = () => {
const database = request.result
if (!database.objectStoreNames.contains('notes')) {
database.createObjectStore('notes', { keyPath: 'id' })
}
}
request.onsuccess = () => {
const database = request.result
const transaction = database.transaction('notes', 'readwrite')
transaction.objectStore('notes').put({ id: 1, text: '离线笔记' })
}
request.onerror = () => {
console.error('IndexedDB 打开失败', request.error)
}
问题 12:什么是 CacheStorage?
难度:⭐⭐⭐
CacheStorage 是管理多个命名 Cache 对象的接口,通常通过全局 caches 访问。Cache 以 Request 为键、Response 为值,常用于 Service Worker 的资源缓存。
CacheStorage 不等同于浏览器 HTTP 缓存,也不是只能在 Service Worker 中使用;Window 和 Worker 上下文在支持时都可以访问。但它不会自动根据 HTTP Cache-Control 进行所有版本管理,缓存何时更新和删除要由应用负责。
常用方法:
caches.open(name):打开或创建命名缓存;cache.match(request)/caches.match(request):查找响应;cache.put(request, response):写入响应;cache.addAll(urls):请求并缓存一批资源;caches.keys()、caches.delete(name):清理旧版本。
响应体通常是流,网络响应在返回页面后如果还要写入缓存,需要使用 response.clone():
async function cacheNetworkResponse(request) {
const response = await fetch(request)
const cache = await caches.open('runtime-v1')
await cache.put(request, response.clone())
return response
}
问题 13:说说 Service Worker 生命周期
难度:⭐⭐⭐⭐
Service Worker 生命周期独立于页面,常见阶段如下:
- 注册:页面调用
navigator.serviceWorker.register(),浏览器检查脚本 URL、源和 scope。 - 安装 installing:触发
install,通常在这里预缓存静态资源;通过event.waitUntil()告知浏览器安装任务何时完成。 - 等待 waiting:安装成功后,如果旧版本仍控制页面,新版本通常等待旧版本退出。
- 激活 activating/activated:触发
activate,适合删除旧缓存、迁移数据;skipWaiting()可以请求跳过等待,但要防止新旧资源不兼容。 - 控制页面:新激活的 worker 通常控制之后打开或重新加载的页面;
clients.claim()可以请求立即接管已打开页面,也应谨慎使用。 - 终止和重新启动:浏览器可以为了节省资源终止 worker,下一次事件到来时再启动它。

一个简化的版本化生命周期示例:
const CACHE_NAME = 'app-shell-v2'
const ASSETS = ['/', '/index.html', '/styles.css', '/app.js', '/offline.html']
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_NAME).then(cache => cache.addAll(ASSETS))
)
// 不建议在不了解新旧页面兼容性的情况下盲目 skipWaiting。
})
self.addEventListener('activate', event => {
event.waitUntil((async () => {
const keys = await caches.keys()
await Promise.all(
keys
.filter(key => key !== CACHE_NAME)
.map(key => caches.delete(key))
)
})())
})
如果 install 中的 Promise 失败,安装会失败;如果新旧版本共享资源名但内容不兼容,贸然 skipWaiting 可能使旧页面加载新资源而出错。
问题 14:如何更新 Service Worker?
难度:⭐⭐⭐⭐
常见更新流程如下:
- 发布新的 Service Worker 脚本或使其内容发生字节变化。
- 浏览器在导航或更新检查时获取新脚本并安装新版本。
- 新版本完成
install后,如果旧页面仍受旧 worker 控制,新版本进入waiting。 - 旧页面关闭或新版本通过明确的更新流程接管后,触发
activate。 - 在
activate中删除不再使用的缓存,并通过页面消息提示用户刷新。
缓存名称或预缓存清单应版本化:
const CURRENT_CACHE = 'app-shell-v3'
self.addEventListener('activate', event => {
event.waitUntil(
caches.keys().then(keys => Promise.all(
keys
.filter(key => key.startsWith('app-shell-') && key !== CURRENT_CACHE)
.map(key => caches.delete(key))
))
)
})
生产环境还要注意 HTTP 缓存对 Service Worker 脚本的影响、回滚策略、资源原子发布,以及 skipWaiting()/clients.claim() 引起的新旧页面不一致。
问题 15:Service Worker 常见缓存策略
难度:⭐⭐⭐⭐
缓存策略要按资源类型、实时性和失败回退设计,常见策略包括:
1. Cache Only(仅缓存)
只从缓存读取,适合明确在安装阶段预缓存的离线资源;缓存没有命中时会失败。

2. Network Only(仅网络)
始终访问网络,不读取 Cache API,适合必须实时获取且不应使用旧数据的请求,例如部分管理操作和写入请求。

3. Stale-While-Revalidate(缓存优先并后台更新)
先快速返回缓存,同时在后台请求网络并更新缓存,适合头像、样式、图片等允许短暂旧数据的资源。要处理缓存未命中、请求失败和并发更新。

4. Cache First(缓存优先)
缓存命中就返回,未命中才请求网络并写入缓存,适合带 hash 的静态资源;对于 HTML 和实时业务数据要谨慎,否则容易显示旧页面。

5. Network First(网络优先)
优先访问网络,网络失败时回退缓存,适合需要较新内容但又要兼容离线的导航或 API 请求。可以设置超时,但超时不是所有环境都自动提供,需要自己实现或使用成熟工具。

Angular、Workbox 等框架和工具会提供类似的策略名称,但具体实现、匹配规则和缓存淘汰策略仍需要查看文档。不要把 Cache API 与 HTTP 缓存、CDN 缓存混为一谈。
问题 16:什么是 App Shell?
难度:⭐⭐⭐⭐
App Shell 是应用启动所需的最小 HTML、CSS、JavaScript 和基础资源。把它预缓存后,应用可以更快显示基本界面,再从网络或本地数据库加载动态内容。
它适合导航相对稳定、内容持续变化的应用,例如后台系统、邮件、社交信息流。App Shell 不是必须采用的架构;SSR、MPA、岛屿架构或普通静态网站也可以通过合理的缓存获得良好体验。不要把完整的动态数据和用户隐私数据盲目预缓存。
问题 17:App Shell 应具有什么特性?
难度:⭐⭐⭐⭐
一个合适的 App Shell 通常应该:
- 快速加载并尽快展示可用界面;
- 使用尽可能少的数据;
- 只缓存确实需要的静态资源;
- 把导航、布局和动态内容分离;
- 在离线时提供明确的状态和回退页面;
- 通过网络、Cache API 或 IndexedDB 获取页面数据;
- 有明确的版本、更新和缓存清理策略;
- 兼顾无障碍、性能、错误处理和安全。
问题 18:苹果设备对 PWA 的支持怎样?
难度:⭐⭐⭐⭐⭐
原文基于 Safari 11.1/iOS 11.3 的历史状态,不能直接作为当前结论。现代 iOS/iPadOS 已支持 Service Worker、Cache API 和将网站添加到主屏幕;iOS/iPadOS 16.4 起,添加到主屏幕的 Web App 支持 Web Push 和通知能力。
仍需注意:
- 能力受 iOS/iPadOS 版本、浏览器和“是否添加到主屏幕”等条件影响;
- Web Push 的权限、用户手势和服务端推送配置仍然必须满足要求;
- 后台同步、周期性同步、文件系统、窗口控制等能力不能按 Chromium 行为推断;
- iOS 的安装入口通常是分享菜单,不能依赖 Chromium 的
beforeinstallprompt; - Safari 的存储配额、回收策略和 WebKit 行为应在目标设备上实测。
结论应写成“按目标版本检查 WebKit 和 MDN 兼容性”,而不是简单地说 iOS 支持或不支持 PWA。
问题 19:Service Worker 能否同时存在多个?
难度:⭐⭐⭐⭐⭐
同一个源可以注册多个 Service Worker,但每个注册都有自己的 scope;同一请求通常由匹配范围最长的注册控制。一个 scope 在同一时间只有一个 active worker 版本,更新时会出现 waiting/activating 等状态。
需要注意:
- Service Worker 的脚本 URL 必须满足同源和 scope 规则;
- 不同 scope 的 worker 可能分别控制不同路径;
- Cache Storage 和 IndexedDB 默认按源共享,不会因为 scope 不同自动隔离;
- 多个 worker 使用相同缓存名称时可能互相覆盖,因此应使用清晰的命名空间和版本策略;
- worker 可能被终止,不能假设每个 tab 都拥有一个长期存活的独立实例。
问题 20:Service Worker 能做而 Web Worker 不能做什么?
难度:⭐⭐⭐⭐⭐
Web Worker 主要用于把计算移到独立线程,减轻页面主线程压力;它通常由页面创建,使用 postMessage 通信,没有 DOM 访问权限。
Service Worker 是面向源和 scope 的事件驱动代理,可以在页面未打开时由浏览器按事件启动。它可以处理受控请求、缓存资源、接收 Push,并在支持的浏览器中参与后台同步等。但 Service Worker 也不能直接操作 DOM,也不是持续运行的通用后台线程。
| 方面 | Web Worker | Service Worker |
|---|---|---|
| 主要用途 | 并行计算、后台处理 | 请求代理、离线缓存、推送和事件驱动后台任务 |
| 生命周期 | 通常由页面/Worker 的拥有者管理 | 由浏览器按注册、事件和资源策略管理,可被终止后重启 |
| 作用范围 | 通常服务创建它的页面 | 按 origin 和 scope 控制多个客户端 |
| 网络拦截 | 不能接管页面所有请求 | 可以处理受控客户端的 fetch 事件 |
| DOM | 无法直接访问 | 无法直接访问 |
| 通信 | postMessage、MessageChannel 等 | Client.postMessage、MessageChannel 等 |
SharedWorker 又是另一种模型,不能把它与普通 Web Worker 或 Service Worker 混为一谈。
问题 21:使用 Service Worker 的 App Shell 架构有哪些优势?
难度:⭐⭐⭐⭐⭐
- 重复访问可以更快:已经缓存的 Shell 不必每次重新下载。
- 离线有基本界面:缓存命中时可以显示导航和回退内容。
- 动态内容与静态资源分离:数据可以按需请求,静态资源可以长期缓存并通过 hash 更新。
- 数据使用更可控:只缓存必要资源,避免把所有大图片和无关页面预缓存。
- 体验接近独立应用:在支持安装和 standalone 显示模式的平台上,可以有独立窗口和启动图标。
代价是需要处理缓存失效、版本升级、存储配额、离线数据冲突和错误回退。App Shell 不是把所有资源“永久缓存”,更不是替代服务端数据一致性的方案。
问题 22:PWA 能拥有真正的持久存储吗?
难度:⭐⭐⭐⭐⭐
原文直接回答“不能”,过于绝对。更准确的说法是:Web 存储受到浏览器配额和用户代理管理,普通 Cache API/IndexedDB 数据可能在存储压力、隐私模式、用户清理或卸载应用时被删除;因此不能把它当作绝对永久存储。
可以查询配额并请求持久化存储:
async function requestPersistentStorage() {
if (!navigator.storage?.persist) return false
const alreadyPersistent = await navigator.storage.persisted()
if (alreadyPersistent) return true
return navigator.storage.persist()
}
requestPersistentStorage().then(persistent => {
console.log(persistent ? '已请求持久化存储' : '仍可能被回收')
})
persist() 是否获准由浏览器策略、用户参与度、安装状态和平台决定;即使获准,用户显式清理数据、卸载应用或系统管理存储时仍不能保证永不丢失。
可靠的离线优先应用通常采用组合方案:
- Cache API 保存可重新下载的静态资源;
- IndexedDB 保存离线队列和结构化业务数据;
navigator.storage.persist()在适合的场景请求持久化;- 关键数据同步到服务器,并处理冲突、重试和身份认证;
- 退出登录时明确清理包含敏感信息的本地数据。
现代实践补充
- 先保证普通 Web 体验,再逐步加入 manifest、Service Worker 和离线能力。
- 使用 Workbox 等工具时,仍需理解 precache、runtime caching、版本清理和更新提示,不要盲目套模板。
- 不缓存带有用户隐私或认证信息的响应;对 API、HTML、图片和带 hash 的静态资源使用不同策略。
- 通过 Lighthouse、浏览器 Application 面板、真实设备和弱网/离线测试验证安装、更新、推送和数据恢复。
- 监控 Service Worker 错误、缓存命中率、离线失败和更新失败;出现问题时应能让用户清除或回滚缓存。
参考资料
- MDN:渐进式 Web 应用
- MDN:让 PWA 易于安装
- MDN:使用 Service Worker
- MDN:CacheStorage
- MDN:IndexedDB API
- MDN:StorageManager.persist()
- web.dev:Learn PWA
- web.dev:Service worker lifecycle
- WebKit:Web Push for Web Apps on iOS and iPadOS
作者:Earl_Lam
链接:https://juejin.cn/post/6844904052166230030
来源:稀土掘金。著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。