标签

PWA

PWA (Progressive Web App)是一种渐进式Web应用程序,它是基于Web技术构建的应用程序,可以像本地应用程序一样运行在各种设备和平台上,包括桌面、移动设备和平板电脑等。 PWA具有许多本地应用程序的功能,例如离线访问、推送通知、主屏幕快捷方式等,同时还具有网页应用程序的优势,例如可访问性、跨平台性等。PWA可以通过Web浏览器安装到设备中,无需通过应用商店进行下载和安装。 PWA使用一组现代Web技术,例如Service Workers、Web App Manifests、HTTPS等,以提供一个类似于本地应用程序的体验。使用PWA,用户可以在设备上安装应用程序,无需访问应用商店,而且在网络不可用时,应用程序仍然可以正常工作。 PWA是一种新兴的Web技术,它正在成为Web应用程序的未来,许多公司和组织正在将其应用于其Web应用程序,以提供更好的用户体验和更高的用户参与度。

PWA
服务端5月31日 20:28
什么是 PWA?它和普通 Web、原生 App 的区别在哪里?PWA,全称 Progressive Web App,直译是渐进式 Web 应用。说白了,它还是一个网站,但在浏览器支持的情况下,可以逐步获得接近原生 App 的体验:能安装到桌面或主屏幕,能离线打开部分页面,能缓存资源提升加载速度,也能在合适场景下发送通知。它不是一个新框架,而是一组 Web 能力和工程实践的组合。 ## PWA 到底“渐进”在哪里 渐进式的意思是:基础体验先保证可访问,再按浏览器能力增强。用户用老浏览器访问,它仍然是普通网页;用户用现代浏览器访问,Service Worker、Manifest、Push API、Background Sync 等能力才逐步启用。这个思路很适合 Web,因为你无法控制每个用户的设备、系统版本和浏览器策略。 PWA 的核心通常包括三块。第一是响应式页面,能适配手机、平板和桌面。第二是 Web App Manifest,告诉浏览器应用名称、图标、启动地址和显示模式。第三是 Service Worker,负责缓存资源、离线回退、请求拦截和更新控制。 ```json { "name": "Demo PWA", "short_name": "Demo", "start_url": "/", "display": "standalone", "icons": [ { "src": "/icon-192.png", "sizes": "192x192", "type": "image/png" }, { "src": "/icon-512.png", "sizes": "512x512", "type": "image/png" } ] } ``` ## 它比普通 Web 强在哪里 普通 Web 最大优势是可搜索、可链接、免安装,弱点是弱网体验差、留存入口浅。PWA 保留了链接和搜索的优势,又通过缓存和安装入口补上体验短板。对工具站、内容站、内部系统、轻量电商来说,用户不用去应用商店,打开网页就能使用,愿意的话再安装到主屏幕。 但 PWA 不是万能替代。它无法完全获得原生 App 的系统权限,iOS 上部分能力也有限制。高强度后台定位、蓝牙深度控制、复杂系统级能力,仍然更适合原生方案。PWA 的边界是“把 Web 体验做得更像 App”,不是“把浏览器变成操作系统”。 ## 它比原生 App 省在哪里 PWA 通常一套 Web 代码覆盖多端,发布也不必每次等应用商店审核。更新可以通过服务器部署和 Service Worker 缓存策略完成,适合频繁迭代。安装包小、入口轻、分享方便,这些都是增长和内容分发上的优势。 代价是平台差异需要认真测试。桌面 Chrome、Android Chrome、iOS Safari 对安装、通知、缓存清理的表现并不完全一样。工程上要做好降级:支持就增强,不支持也不能影响核心功能。 ## 落地时先看业务场景 做 PWA 前先确认用户有没有复访、弱网和安装入口需求。如果只是一次性营销页,完整 PWA 可能反而增加维护成本。 ## 追问 ### PWA 和响应式网站是一回事吗? 不是,响应式只解决不同屏幕上的布局问题。PWA 在响应式之外,还强调可安装、离线能力、缓存策略和类原生启动体验。一个网站可以是响应式的,但没有 Manifest 和 Service Worker,它通常不能算完整 PWA。取舍上可以先做好响应式,再逐步加 PWA 能力,不必一次改完。 ### PWA 能完全替代原生 App 吗? 不能一概而论,轻量工具、内容阅读、内部系统很适合 PWA。需要深度系统权限、复杂后台任务或高性能图形能力的产品,原生 App 仍然更稳。PWA 的优势是低摩擦和跨平台,原生的优势是权限、性能和系统整合。判断时不要只看开发成本,也要看业务是否依赖平台能力。 ### PWA 必须支持离线吗? 严格说,PWA 至少应该在关键路径上有离线或弱网降级能力。不是所有功能都要离线可用,支付、实时聊天、库存查询这些功能本来就需要网络。更现实的做法是缓存应用壳、历史内容和离线提示页。踩坑点是宣传“离线可用”,结果断网后核心页面空白,这比不做离线更伤用户信任。 ### PWA 对 SEO 有帮助吗? PWA 本身不会直接让排名上升,但它能改善加载速度、可访问性和用户体验,这些会间接影响搜索表现。内容仍然要能被搜索引擎抓取,不能把核心文本藏在登录后或只靠客户端异步渲染。边界是:PWA 是体验增强,不是 SEO 魔法。做内容站时,服务端渲染或静态生成仍然很重要。 ### 什么项目不适合优先做 PWA? 如果产品核心依赖系统通讯录、后台定位、复杂蓝牙、长时间后台运行,PWA 可能会受很多限制。如果团队没有能力维护缓存更新流程,也容易把用户卡在旧版本。短期活动页、一次性落地页通常不需要完整 PWA,只要做好性能和移动端适配即可。PWA 适合有复访、有弱网场景、又希望降低安装门槛的产品。
服务端5月31日 20:28
Service Worker 生命周期怎么跑?注册、安装和激活有哪些坑?Service Worker 是浏览器放在页面和网络之间的一层后台脚本。它不属于某个页面,也不能直接操作 DOM,但可以拦截请求、管理缓存、处理推送和后台同步。PWA 里的离线访问、秒开体验、资源更新,大多都绕不开它。理解 Service Worker 的关键不是背 API,而是搞清楚它什么时候安装、什么时候激活、什么时候真正开始控制页面。 ## 生命周期怎么走 页面先注册 Service Worker。注册成功不代表它马上接管当前页面,只是告诉浏览器这个作用域下有一个后台脚本。脚本下载后进入 install 阶段,通常在这里预缓存应用壳、离线页和必要静态资源。 ```javascript if ('serviceWorker' in navigator) { window.addEventListener('load', async () => { try { const reg = await navigator.serviceWorker.register('/sw.js', { scope: '/' }); console.log('service worker scope:', reg.scope); } catch (err) { console.error('register failed', err); } }); } ``` install 成功后,新 Worker 默认会等待旧 Worker 退出。所有旧页面关闭后,它才进入 activate。activate 阶段适合清理旧缓存、迁移数据、准备接管页面。真正激活后,它才能处理 `fetch`、`push`、`sync` 等事件。 ```javascript const CACHE = 'app-v4'; self.addEventListener('install', event => { event.waitUntil(caches.open(CACHE).then(c => c.addAll(['/', '/offline.html']))); }); self.addEventListener('activate', event => { event.waitUntil( caches.keys().then(keys => Promise.all(keys.filter(k => k !== CACHE).map(k => caches.delete(k)))) ); }); ``` ## 它能做什么 最常见的功能是请求拦截。你可以按资源类型选择缓存策略:静态资源 Cache First,HTML Network First,非强一致接口 Stale While Revalidate。它也可以提供离线页,让网络断开时不至于出现浏览器默认错误页。 Service Worker 还能处理 Web Push 通知和 Background Sync,但这些能力有明显边界。通知需要用户授权,后台同步受浏览器策略和电量优化影响,不能当成一定会准时执行的定时任务。项目里要把它当作增强能力,而不是业务唯一链路。 ## 更新流程为什么要谨慎 Service Worker 更新最怕“半新半旧”。用户打开的页面可能还加载着旧 JS,但新 Worker 已经把缓存换成了新资源,这时点击某个懒加载模块就可能 404。大型前端应用通常会检测到新版本后提示刷新,而不是静默强制切换。缓存清理也要保守,只删确定不用的版本;如果把当前页面依赖的资源删掉,弱网下问题会被放大。还要注意 CDN 缓存,`sw.js` 本身不能被长时间强缓存,否则你以为发布了新策略,用户浏览器却一直拿着旧脚本。生产环境通常会让 `sw.js` 使用短缓存或 no-cache,而静态资源用 hash 文件名长期缓存。 ## 常见限制 Service Worker 要求 HTTPS,localhost 例外。它有作用域限制,`/app/sw.js` 默认只能控制 `/app/` 下的页面,如果希望控制全站,通常放在根路径。它不能访问 DOM,和页面通信要用 `postMessage`。缓存空间也不是无限的,浏览器可能在存储压力大时清理数据。 ## 追问 ### 为什么注册成功后刷新一次才生效? 因为新注册的 Service Worker 默认不会立刻控制已经打开的页面。第一次加载页面时还没有控制者,注册完成后通常要等下一次导航才开始拦截请求。可以用 `clients.claim()` 让激活后的 Worker 尽快接管页面,但这也可能让当前页面突然使用新缓存策略。取舍是更新速度和稳定性,复杂应用不要盲目强接管。 ### skipWaiting 和 clients.claim 要不要一起用? 它们可以一起用,但不是所有项目都应该用。`skipWaiting()` 会让新 Worker 跳过等待,`clients.claim()` 会让它激活后接管现有页面。好处是更新快,坏处是旧页面的 JS 和新缓存可能混在一起,出现接口或资源版本不匹配。发布包含破坏性变更时,更稳的做法是提示用户刷新。 ### Service Worker 可以缓存 POST 请求吗? Cache API 本身主要面向 GET 请求,直接缓存 POST 往往不合适。提交订单、登录、支付这类请求有副作用,缓存会制造非常难排查的问题。离线提交可以用 IndexedDB 暂存数据,等网络恢复后再同步,但要处理幂等、失败重试和冲突。边界是“可延迟的草稿”可以离线,“必须立即确认的交易”不要伪装成成功。 ### 为什么作用域会导致 fetch 不触发? Service Worker 只能控制自己 scope 范围内的页面。脚本放在 `/pwa/sw.js`,默认控制不了 `/tools` 页面,自然也拦截不到那些页面发出的请求。可以通过注册时设置 scope,但前提是服务器允许对应路径。踩坑时先在 DevTools 里看当前页面的 controller,而不是只看注册是否成功。 ### 调试 Service Worker 最该看哪里? Chrome DevTools 的 Application 面板是第一入口,可以看注册状态、生命周期、缓存和推送事件。Network 面板里如果资源来自 Service Worker,会有明确标识。调试时常见坑是勾选了 Update on reload,却忘了线上没有这个行为。测试发布流程时要用真实构建和普通刷新方式验证,不能只依赖开发模式。
服务端5月31日 20:28
Web App Manifest 怎么配置?哪些属性影响 PWA 安装体验?Web App Manifest 是 PWA 的应用说明书。浏览器会读取这个 JSON 文件,决定应用能不能被安装、安装后叫什么名字、从哪个地址启动、图标长什么样、打开时像浏览器标签页还是独立应用。它本身不负责离线缓存,离线能力要靠 Service Worker;Manifest 解决的是“这个 Web 应用能不能像 App 一样出现在设备上”。 ## 一个可用的 Manifest 示例 Manifest 通常放在站点根目录,并在 HTML 里引用。最小可用配置不复杂,但图标、`start_url`、`scope`、`display` 这些字段会直接影响安装体验。 ```html <link rel="manifest" href="/manifest.webmanifest"> <meta name="theme-color" content="#0f172a"> ``` ```json { "name": "Levenx Tools", "short_name": "Tools", "description": "常用开发工具集合", "start_url": "/?source=pwa", "scope": "/", "display": "standalone", "background_color": "#ffffff", "theme_color": "#0f172a", "lang": "zh-CN", "icons": [ { "src": "/icons/icon-192.png", "sizes": "192x192", "type": "image/png" }, { "src": "/icons/icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "any maskable" } ] } ``` ## 关键属性分别管什么 `name` 是完整应用名,常出现在安装弹窗里;`short_name` 用在主屏幕图标下方,太长会被截断。`start_url` 决定用户从桌面图标打开时进入哪个页面,常用来加来源参数,但不能写成需要登录态才能访问的临时地址。`scope` 控制应用边界,超出边界的链接会回到浏览器里打开。 `display` 决定窗口形态。多数业务应用选择 `standalone`,看起来像独立 App;内容型网站可以保留 `browser`,减少用户迷路。`fullscreen` 更适合游戏、展厅或沉浸式页面,不适合需要地址栏安全感的交易类场景。 图标是最容易被忽略的部分。Android 更依赖 192 和 512 尺寸,maskable 图标能适配圆形、圆角矩形等不同启动器。踩坑点是把普通图标直接声明成 maskable,结果边缘被裁掉,桌面上看起来像被剪了一块。 ## 安装体验还受哪些因素影响 Manifest 写得完整,不代表用户一定愿意安装。应用首屏速度、离线回退、登录后入口是否稳定,都会影响安装后的真实体验。`start_url` 如果带了活动参数,活动结束后可能打开废弃页面;图标如果没有透明边距,在部分启动器上会显得很廉价。更稳的做法是把 Manifest 当成产品入口配置,而不是前端构建时顺手生成的文件。团队还要约定图标和主题色的更新流程,因为这些内容在已安装应用里刷新并不及时。多语言站点最好按语言提供合适的 `name`、`description` 和截图,否则安装弹窗里出现混合语言,会让用户怀疑应用是否正规。 ## 调试 Manifest Chrome DevTools 的 Application 面板能看到 Manifest 解析结果、图标预览和安装条件。安装失败时先看控制台警告,再检查 HTTPS、Service Worker、图标尺寸、`start_url` 是否可访问。iOS Safari 对 Manifest 支持不完整,仍然会受 `apple-touch-icon`、`apple-mobile-web-app-capable` 等标签影响,所以移动端不要只测 Chrome。 ## 追问 ### Manifest 配好了就一定会出现安装提示吗? 不一定,Manifest 只是必要条件之一。浏览器还会看 HTTPS、Service Worker、访问频率、图标和页面可用性等条件。不同浏览器的安装入口也不一样,有些不会主动弹提示,只在菜单里提供安装。取舍上不要把业务转化完全压在自动弹窗上,页面内最好有自己的安装引导。 ### start_url 和 scope 有什么区别? `start_url` 是从桌面图标启动时打开的入口,`scope` 是 PWA 能控制的路径范围。比如 `start_url` 是 `/app`,`scope` 是 `/`,用户仍然可以在独立窗口里访问站内其他路径。如果 `scope` 设得太窄,点到详情页可能突然跳回浏览器。这个坑在多目录站点里很常见,发布前要从安装入口完整走一遍。 ### display 应该选 standalone 还是 fullscreen? 大多数 PWA 选 `standalone` 更稳,因为它去掉浏览器地址栏,但保留系统级窗口语义。`fullscreen` 会让应用占满屏幕,适合游戏和大屏展示,但用户可能不知道如何返回或确认当前页面来源。交易、登录、内容阅读类应用不建议轻易全屏。这里的取舍是沉浸感和可控感,越沉浸越要自己补导航。 ### 图标为什么要配置 maskable? 不同 Android 启动器会把图标裁成不同形状,maskable 图标能给系统预留安全区域。普通正方形图标如果被强行裁切,Logo 可能贴边或缺角。边界是并非所有平台都使用 maskable,但配置好不会影响普通图标。实际制作时要把主体放在中心安全区,别把文字和品牌符号贴到边缘。 ### Manifest 更新为什么有时不生效? Manifest 会被浏览器缓存,桌面已安装应用也不会每次都立刻刷新元数据。改了图标或名称后,可能需要重新安装、清缓存或等待浏览器更新。不要频繁改 `start_url` 和应用名,这会让用户和统计数据都变得混乱。线上调整时先在测试域名验证,再逐步发布到正式环境。
服务端5月31日 20:28
PWA 缓存策略怎么选?不同资源该用哪种缓存方式?PWA 缓存策略不是把资源一股脑塞进 Cache Storage。真正要做的是按资源的变化频率、实时性要求和离线价值分开处理:静态资源优先速度,接口数据优先新鲜度,关键页面要能降级,支付、库存、权限这类请求通常不要缓存。选错策略的后果很直接,用户可能看到旧价格、旧头像,或者离线时连一页友好的提示都没有。 ## 常见缓存策略怎么选 `Cache First` 适合版本号稳定的 JS、CSS、字体、Logo、插图。它先查缓存,命中就返回,没命中再请求网络。好处是快,离线也能用;边界是资源必须有版本管理,否则你发布了新包,用户还可能拿到旧文件。 ```javascript self.addEventListener('fetch', event => { const url = new URL(event.request.url); if (url.pathname.startsWith('/assets/')) { event.respondWith( caches.match(event.request).then(hit => hit || fetch(event.request)) ); } }); ``` `Network First` 适合 HTML 文档、用户资料、订单列表、文章详情等需要尽量新的内容。它先请求网络,失败时再回退到缓存。这个策略的取舍是首屏会受网络影响,所以最好配合超时控制,不要让用户在弱网下等到浏览器自己放弃。 `Stale While Revalidate` 适合头像、配置、推荐列表、非强一致的内容。它先返回缓存,让页面马上有东西显示,同时后台请求新数据并更新缓存。踩坑点是用户短时间内可能看到旧内容,因此不要用在支付状态、库存数量、风控结果这种需要准确性的场景。 `Network Only` 用在登录、支付、实时协作、埋点上报等请求。它不走缓存,保证语义清楚。`Cache Only` 则适合离线页、预缓存的壳资源,前提是安装阶段已经把这些资源放进缓存。 ## 推荐的混合配置 实际项目里一般不会只用一种策略。常见做法是静态资源 Cache First,HTML Network First,非关键接口 Stale While Revalidate,敏感接口 Network Only,离线页 Cache Only。缓存名要带版本号,激活新 Service Worker 时清理旧缓存,否则用户设备上会越积越多。 ```javascript const CACHE = 'pwa-cache-v3'; const PRECACHE = ['/', '/offline.html', '/assets/app.css']; self.addEventListener('install', event => { event.waitUntil(caches.open(CACHE).then(cache => cache.addAll(PRECACHE))); }); self.addEventListener('activate', event => { event.waitUntil( caches.keys().then(keys => Promise.all(keys.filter(k => k !== CACHE).map(k => caches.delete(k))) ) ); }); ``` ## 上线前还要检查什么 缓存策略上线前要单独测三种状态:首次访问、已有旧缓存后访问、断网后访问。很多问题只在第二种状态出现,例如 HTML 已更新但旧 JS 还在运行,页面会报接口字段不存在。接口响应如果要进入缓存,最好先检查 `response.ok`,不要把 500 错误页也缓存进去。跨域资源还要注意 opaque response,它看不到状态码,缓存前要确认这个资源真的值得长期保存。 ## 追问 ### Cache First 和 Network First 最大区别是什么? Cache First 把速度放在第一位,Network First 把内容新鲜度放在第一位。静态资源通常不常变,用 Cache First 能明显减少加载时间。动态接口如果也用 Cache First,最容易出现用户看到旧数据的问题。实际取舍要看“旧一点是否可接受”,而不是看哪种策略更高级。 ### Stale While Revalidate 适合所有接口吗? 不适合,它适合允许短暂过期的数据,比如推荐列表、用户头像、公共配置。它的边界是第一次没有缓存时仍然要等网络,之后才会体现“秒开”。踩坑最多的是把它用在订单状态或余额上,用户看到旧结果会产生信任问题。强一致接口宁可慢一点,也不要返回旧值。 ### 缓存版本号应该怎么设计? 缓存名最好跟构建版本或资源清单版本绑定,例如 `pwa-cache-v20260531`。如果资源 URL 已经带 hash,可以让静态资源长期缓存,发布时自然换 URL。不要每次刷新都换缓存名,那会让缓存失去意义,也会增加清理压力。版本升级时重点清旧缓存,但不要误删当前页面还在使用的资源。 ### 离线页面应该缓存哪些内容? 至少缓存一个 `/offline.html`、基础样式和必要图标,让断网时有明确反馈。不要试图把整个站点都预缓存,移动端存储空间和安装速度都会受影响。边界是“离线能完成核心阅读或提示”即可,不必让所有功能离线可用。踩坑点是离线页引用了未缓存的 CSS 或图片,结果断网时页面仍然是坏的。 ### 如何避免缓存把线上问题放大? 发布前要在 DevTools 的 Application 面板测试更新流程,确认旧缓存会被清理。关键资源建议使用带 hash 的文件名,接口缓存要设置白名单,不要按域名全部缓存。遇到线上故障时,可以通过提升缓存版本和返回 no-store 响应来切断旧内容。缓存能提升体验,也会让错误保留更久,所以策略越激进,回滚方案越要提前准备。
服务端5月31日 17:42
PWA 离线功能怎么做才能避免缓存旧页面?PWA 离线功能的核心不是“断网也能打开页面”,而是让用户在弱网、断网、恢复联网之间不丢数据、不看错数据。Service Worker 拦截请求,Cache API 存静态资源,IndexedDB 存业务数据。难点在取舍:哪些内容能离线读,哪些必须实时请求,哪些失败后要明确提示。 ## 追问 ### 离线功能最小可用版本应该包含什么? 最小版本包括 Service Worker 注册、应用外壳预缓存、离线兜底页和网络状态提示。应用外壳指 HTML、核心 CSS、入口 JS、图标和 offline.html,不是全站所有资源。取舍上,预缓存越多首次安装越慢,也更容易导致 install 失败。建议先保证核心路径能打开,再逐步给文章、表单草稿加离线能力。 ```js const CACHE = 'app-shell-v1'; self.addEventListener('install', e => { e.waitUntil(caches.open(CACHE).then(c => c.addAll([ '/', '/offline.html', '/app.css', '/app.js' ]))); }); ``` ### fetch 缓存策略应该怎么选? 静态资源适合 cache first,因为文件名通常带 hash,命中缓存速度最快。HTML 更适合 network first,失败后再返回缓存或 offline.html,避免用户长期停留在旧页面。接口数据要按业务分级:文章详情可 stale-while-revalidate,余额、权限、库存不要离线伪造。踩坑是用一个通用 caches.match 处理所有请求,结果 POST、鉴权接口和跨域资源都被误缓存。 ```js self.addEventListener('fetch', e => { if (e.request.method !== 'GET') return; if (e.request.mode === 'navigate') { e.respondWith(fetch(e.request).catch(() => caches.match('/offline.html'))); } }); ``` ### 离线写入数据怎么避免丢失? 离线提交不要直接失败,可以先把用户输入保存到 IndexedDB,并标记 pending 状态。恢复联网后再用 Background Sync 或前台重试队列同步。边界是 Background Sync 兼容性并不完美,不能把它当成唯一兜底,页面重新打开时也要主动检查待同步数据。服务端还要支持幂等键,否则弱网重试可能造成重复订单或重复扣费。 ### 缓存更新最容易踩什么坑? 最常见的是新 Service Worker 已安装但旧页面仍由旧版本控制,用户刷新几次仍看到旧资源。skipWaiting 和 clients.claim 可以加快接管,但也可能让正在使用的页面突然加载到新旧混合资源。取舍上,内容型站点可以更积极更新,复杂后台系统最好提示用户刷新。每次发布都应改变资源 hash,并在 activate 阶段清理旧缓存。 ### 怎么测试 PWA 离线能力是否可靠? Chrome DevTools 的 Offline 模式只能覆盖一部分情况,还要测试慢 3G、接口超时、DNS 失败和缓存被清理。移动端尤其要测首次访问、二次访问、更新后访问和久未打开后的访问。踩坑是本地 localhost 下表现正常,线上 HTTPS、CDN 缓存和 Service Worker scope 不一致导致注册失败。验收时看四项:断网能否打开、离线页是否出现、草稿是否保留、恢复网络后是否同步。
服务端5月31日 17:42
PWA 推送通知怎么实现才不容易被用户拒绝?PWA 推送通知的技术链路并不复杂:页面请求权限,PushManager 创建订阅,服务端保存 endpoint 和密钥,Service Worker 收到 push 后展示通知。真正难的是边界和时机:过早弹权限会被拒绝,订阅过期不清理会浪费推送额度,通知没价值会被系统静音。 ## 追问 ### 推送通知必须有哪些组件? 至少需要 Notification API、Push API、Service Worker、服务端逻辑和 VAPID 密钥。Notification API 负责展示通知,Push API 负责订阅浏览器推送通道,Service Worker 负责后台接收 push 事件。取舍上,前端只做订阅和展示,定向发送、频控和权限状态管理应该放在服务端。边界是 Web Push 不是 WebSocket,它不能保证实时到达,也不适合承载大块业务数据。 ### 什么时候请求通知权限比较合适? 最好在用户完成一次明确动作后再请求,比如关注价格、订阅课程更新、打开“提醒我”开关。页面加载时直接请求是常见踩坑,用户还不了解价值就会关掉权限。权限一旦被拒绝,很多浏览器不会让你反复弹窗,只能引导用户去设置里改。实际项目里先用站内弹层解释价值,用户确认后再调用 Notification.requestPermission。 ```js async function subscribe(registration, publicKey) { if (Notification.permission !== 'granted') return null; return registration.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: urlBase64ToUint8Array(publicKey) }); } ``` ### 服务端发送推送时要注意什么? 服务端要保存 subscription 的 endpoint、p256dh、auth,还要记录用户、设备和订阅时间。发送失败时不能只打日志,410 或 404 通常表示订阅失效,需要从数据库删除。取舍上,群发通知要做队列和限速,不要在一次 HTTP 请求里同步推给所有人。通知 payload 建议只放标题、摘要和跳转地址,详情打开页面后再拉接口。 ```js webpush.setVapidDetails('mailto:ops@example.com', publicKey, privateKey); await webpush.sendNotification(subscription, JSON.stringify({ title: '订单状态更新', body: '你的订单已发货', url: '/orders/123' })); ``` ### iOS、Safari 和桌面浏览器有什么差异? Chrome、Edge、Firefox 支持较成熟,Safari 和 iOS 限制更多。iOS 需要用户把网站添加到主屏幕后才可接收 Web Push,低版本还不支持。踩坑是开发时只在 Chrome 桌面调通,到了 iPhone 上发现根本没有订阅入口。上线前要写清兼容性,不支持的设备用短信、邮件或站内消息兜底。 ### 通知点击和频率怎么设计? notificationclick 应优先复用已有窗口,避免每次点击都打开新标签。频率上要有业务上限,比如营销通知每天不超过一次,交易通知只发关键状态变化。边界是 requireInteraction、震动、声音等能力不能滥用,过强打扰会让用户永久关闭权限。更稳的做法是提供通知偏好设置,让用户选择提醒类型。
服务端5月31日 17:42
PWA 性能优化应该先改缓存还是先改资源加载?PWA 性能优化不要一上来就把所有资源塞进缓存。更稳的做法是先看瓶颈:首屏慢,多半是 JavaScript、CSS、字体和图片阻塞;二次访问慢,再重点看 Service Worker 缓存;交互卡顿,则要拆主线程任务。PWA 的优势是“像 App 一样快”,但缓存策略写错,用户看到的可能是旧页面,甚至是旧接口数据。 ## 追问 ### 首屏性能应该先优化哪些资源? 首屏优先处理 HTML、关键 CSS、入口 JS、首屏图片和字体,因为它们直接影响 FCP、LCP 和 INP。取舍上,不建议把所有 CSS 都内联,内联过多会让 HTML 变胖。比较稳的边界是只内联首屏必需样式,其他样式用 preload 或按路由拆包。图片要用 WebP/AVIF,并明确 width 和 height,避免 LCP 图片加载晚或造成布局抖动。 ```html <link rel="preload" href="/hero.webp" as="image"> <img src="/hero.webp" width="720" height="360" fetchpriority="high" alt="PWA 首页"> ``` ### Service Worker 缓存是不是越多越好? 不是,缓存越多,更新和存储压力越大,移动端还可能被浏览器清理。静态资源适合 cache first,但 HTML 和接口更适合 network first 或 stale-while-revalidate。踩坑最多的是把 `/api/user` 这类强实时接口放进长期缓存,用户退出登录后还能看到旧数据。建议给缓存分层命名,例如 static-v3、runtime-v3,并在 activate 阶段清掉旧版本。 ```js self.addEventListener('fetch', e => { const url = new URL(e.request.url); if (url.pathname.startsWith('/assets/')) e.respondWith(cacheFirst(e.request)); else if (url.pathname.startsWith('/api/')) e.respondWith(networkFirst(e.request)); }); ``` ### 代码分割和预缓存怎么取舍? 代码分割能降低首屏包体,但切得太碎会增加请求数,弱网下反而更慢。预缓存适合 shell、核心路由和稳定静态资源,不适合频繁变化的业务 chunk。一个实用边界是:首屏必需的 chunk 预缓存,低频页面按需加载,高频但非首屏的页面用 prefetch。上线后要看真实用户数据,而不是只看本地 Lighthouse。 ### 图片、字体和第三方脚本有哪些常见坑? 图片不要只做 lazy loading,首屏大图懒加载会直接拖慢 LCP。字体建议使用 font-display: swap,并只加载实际用到的字重。第三方脚本要能延后就延后,统计、客服、广告脚本经常是 INP 变差的主因。边界是支付、登录风控这类关键脚本不能随便 defer,需要按业务路径单独评估。 ### 怎么判断优化真的有效? 不要只看一次 Lighthouse 分数,至少要同时看实验室数据和线上 Web Vitals。LCP、CLS、INP、TTFB 能覆盖大部分 PWA 体验问题,缓存命中率和 Service Worker 更新失败率也要一起看。踩坑是只在强网桌面环境测试,结果移动端 4G 下 JS 执行和图片解码完全不同。建议把 web-vitals 上报到日志系统,按路由、设备和网络类型分组看趋势。
服务端5月31日 17:42
PWA 安全性如何保障才算到位?PWA 的安全底线比普通网页更高,因为它能被安装、能离线运行,还可能处理推送、缓存和本地数据。安全不是给页面加一个 HTTPS 就结束,而是要同时管住传输、脚本来源、Service Worker 作用域、缓存内容、身份凭证和监控告警。最重要的原则是:Service Worker 能拦截请求,所以它本身必须像后端入口一样谨慎对待;本地缓存能提升体验,也可能把过期页面、敏感接口响应和错误权限一起保存下来。 ## 必做的安全配置 首先,生产环境必须全站 HTTPS,localhost 只适合开发调试。建议开启 HSTS,但第一次上线不要立刻加 `preload`,确认所有子域都支持 HTTPS 后再提交,否则某个历史子域会被浏览器强制 HTTPS 访问失败。其次,CSP 要尽量收紧脚本来源,少用 `unsafe-inline`,第三方统计、客服、广告脚本要单独评估。安全响应头还应补上 `nosniff`、合理的 Referrer Policy 和必要的 frame 限制,避免浏览器在边界场景里做出宽松解释。 ```nginx add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; add_header Content-Security-Policy "default-src 'self'; script-src 'self' https://cdn.example.com; connect-src 'self' https://api.example.com; img-src 'self' data: https:; object-src 'none'; base-uri 'self'; frame-ancestors 'none'" always; add_header X-Content-Type-Options "nosniff" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; ``` Service Worker 作用域也要控制。放在根目录的 `sw.js` 可以控制整站,适合单应用站点;如果同域下有管理后台、支付页或多个业务线,最好用 `/app/sw.js` 和 `scope: '/app/'` 限定范围。缓存时只缓存同源、GET、非敏感响应,接口响应尤其要区分公开数据和用户私有数据。 ```js self.addEventListener('fetch', event => { const req = event.request; const url = new URL(req.url); if (req.method !== 'GET' || url.origin !== location.origin) return; if (url.pathname.startsWith('/api/private')) return; event.respondWith(caches.match(req).then(r => r || fetch(req))); }); ``` ## 本地数据要按风险分层 PWA 经常用 Cache Storage、IndexedDB 和 Cookie 保存状态,但它们解决的是可用性,不自动解决安全性。公开静态资源可以放心缓存,公开列表数据可以设置短过期,用户私有数据则要绑定身份、过期时间和登出清理。真正敏感的数据,例如长期令牌、身份证明、支付信息,不应该长期留在前端;如果业务必须离线保存,也要用 Web Crypto 加密,并把密钥生命周期设计清楚。这里的边界是:前端设备一旦被用户或恶意脚本控制,本地加密只能降低泄露概率,不能替代服务端权限校验。 ## 追问 ### Token 应该放 localStorage、Cookie 还是 IndexedDB? 没有一个选择适合所有场景。`localStorage` 读取方便,但一旦 XSS 成功,Token 很容易被直接拿走;HttpOnly Cookie 防读取更强,但要认真处理 CSRF、SameSite 和跨域配置。IndexedDB 适合存离线数据,不代表天然安全,敏感数据仍应加密并控制生命周期。取舍是:高安全业务优先短期访问令牌 + HttpOnly Refresh Cookie,普通工具也至少不要把长期 Token 明文塞进 localStorage。 ### PWA 离线缓存会带来哪些安全坑? 最大的坑是把用户私有接口、过期权限页面或错误版本的配置缓存下来。用户退出登录后,如果离线页还能展示旧订单、旧资料,就不是体验问题,而是数据泄露问题。边界做法是缓存静态资源和公开内容,私有数据缓存必须绑定用户、过期时间和登出清理。版本升级时还要删除旧缓存,否则修复过的 XSS 页面可能继续从缓存里被打开。 ### CSP 配得越严越好吗? 方向上越严越安全,但不能不测试就上线。很多 PWA 依赖构建工具注入运行时代码、第三方 CDN、Web Worker 或字体资源,CSP 一刀切会导致白屏、推送脚本失败或埋点丢失。建议先用 `Content-Security-Policy-Report-Only` 收集违规报告,再逐步切到强制模式。踩坑点是为了省事加回 `unsafe-inline` 和 `*`,这会让 CSP 的防护价值大幅下降。 ### 推送通知安全要注意什么? VAPID 私钥只能在服务端保存,前端只暴露公钥。服务端保存订阅对象时要校验 endpoint、p256dh、auth 字段,并把订阅和用户身份、设备、撤销状态关联起来。不要把营销推送和安全推送混在一起,否则用户关闭通知后,真正重要的登录风险提醒也送不到。边界是:推送内容不要携带敏感明文,通知点击后再回到 HTTPS 页面拉取详情。 ### 如何发现 PWA 安全问题已经在线上发生? 只靠 Lighthouse 不够,它更像上线前体检。线上要记录 Service Worker 安装失败、controllerchange 异常、CSP violation、异常 401/403、缓存命中旧版本等事件。日志里不要上传 Token、Cookie 或完整个人数据,只保留排查所需的匿名标识和版本号。安全监控的取舍是信息越多越好排查,但采集过度本身也会变成隐私风险。 ## 收束 PWA 安全的重点,是承认它已经不只是一个普通网页。HTTPS、CSP、受控的 Service Worker、谨慎的缓存策略、合理的 Token 存储和线上监控,缺一块都可能把“离线可用”和“快速更新”变成风险。真正可靠的方案通常不会追求前端万能,而是让前端只保存必要状态,把最终权限、敏感数据和审计证据留在服务端。
服务端5月31日 17:42
PWA 和原生应用怎么选才不踩坑?PWA 和原生应用没有绝对胜负,关键看你的产品最依赖什么。PWA 适合内容、电商、工具、活动页和需要快速试错的业务;原生应用适合高频使用、强交互、重性能、深度调用设备能力的业务。最容易踩坑的决策,是只看开发成本:PWA 省掉了双端开发和商店审核,但如果后期补相机、蓝牙、后台定位、复杂推送,省下的钱可能会在兼容性和体验上还回去。 ## 先看能力边界,再看预算 PWA 的优势是分发轻、更新快、SEO 友好,一条 URL 就能触达用户。它可以离线缓存、添加到桌面、接收推送,并用一套 Web 技术覆盖多端。但它仍受浏览器和系统限制,尤其是 iOS 上的后台任务、推送能力、存储配额和安装引导都比原生更保守。 原生应用的优势是性能稳定、系统能力完整、交互细节可控。地图导航、实时音视频、图片视频编辑、运动健康、IoT 控制、金融安全这类产品,往往需要更深的系统集成。代价是研发、测试、审核、灰度和版本兼容都更重,功能发版不能像 Web 一样随时上线。 ```js function chooseAppType(req) { if (req.needBackgroundLocation || req.needBluetooth) return 'native'; if (req.highPerformanceUI || req.appStoreBusiness) return 'native'; if (req.seoTraffic || req.fastIteration || req.lowBudget) return 'pwa'; return 'hybrid: PWA validate first, native for heavy users'; } ``` ## 对比时别只看“能不能做” 很多能力 PWA 也能做一部分,例如摄像头、定位、离线缓存和通知,但“能调用 API”和“在所有目标设备上稳定好用”不是一回事。原生应用在权限说明、后台执行、系统通知样式和性能调优上更可控,适合关键链路很长的产品。PWA 更像轻装上阵,适合先把核心价值交付出去,再根据数据决定是否投入原生。预算有限时,PWA 是降低试错成本的好选择;需求已经验证且用户每天高频打开时,原生投入更容易回本。 ## 追问 ### 哪些业务优先选 PWA? 内容站、电商落地页、企业门户、轻量工具和 MVP 更适合先做 PWA。它们的核心诉求通常是“尽快被访问、尽快更新、尽量少安装阻力”,而不是榨干设备性能。取舍是你要接受浏览器能力边界,比如后台同步和推送在不同平台表现不完全一致。如果用户主要来自搜索、广告或分享链接,PWA 往往比先上应用商店更划算。 ### 哪些业务不建议只做 PWA? 高性能游戏、短视频剪辑、实时导航、运动轨迹、蓝牙设备控制、银行支付类应用不建议只靠 PWA。不是 Web 完全做不了,而是稳定性、权限能力和后台可靠性很难达到原生水平。边界判断可以很简单:如果一个关键功能必须在锁屏、弱网、长时间后台或硬件高负载下稳定运行,原生胜率更高。否则上线后会变成“多数功能能用,关键时刻不好用”。 ### 混合方案是不是折中就一定更好? 不一定。混合方案可以用 PWA 做获客和试用,用原生承载高频重功能,也可以用 Capacitor/Ionic 把 Web 能力包进壳里。问题是它会引入两套边界:Web 的兼容性和原生壳的发布复杂度都要维护。适合的场景是业务已经验证成立,但团队暂时不想完全重写;如果需求本来很简单,混合方案反而会把架构做重。 ### 决策时最容易忽略什么? 最容易忽略的是用户获取渠道和更新节奏。靠搜索、社媒、广告投放转化的产品,PWA 的 URL 分发和 SEO 是实际优势;靠会员留存、高频打开、系统通知召回的产品,原生应用更容易建立习惯。还有一个踩坑点是“普通用户不一定知道如何安装 PWA”,所以不能把桌面安装率当成默认结果。上线前最好用真实设备验证安装提示、离线页、推送授权和首屏性能,而不是只在 Chrome DevTools 里看通过率。 ### 用什么指标判断选型是否选对? 如果选 PWA,要重点看首屏加载、安装提示转化、离线访问成功率、搜索流量转化和更新触达率。如果选原生,要看商店下载转化、激活率、留存、崩溃率、推送召回和版本升级覆盖率。不要只用开发周期衡量,因为上线快但留不住用户,依然不是好选型。更靠谱的做法是先列出三条不可失败的业务链路,再判断哪个技术路线更能保护这些链路。 ## 收束 如果产品还在验证阶段、主要靠链接传播、功能以浏览和轻交互为主,PWA 通常更合适。如果业务已经证明高频、强交互、深度依赖设备能力,原生应用更稳。介于两者之间时,可以先用 PWA 验证需求,再把最重的能力迁到原生或混合壳里,不必一开始就把选择做死。
服务端5月31日 17:42
PWA 如何让用户及时更新到最新版本?PWA 更新的核心不是“部署完用户就立刻看到新代码”,而是要理解 Service Worker 的生命周期:新脚本会先 install,再进入 waiting,只有旧页面关闭或主动 skipWaiting 后才会 activate。真正稳妥的做法是:HTML 走网络优先,带 hash 的静态资源走缓存优先,发现新 Service Worker 后提示用户刷新,而不是悄悄把正在使用的页面换掉。这样牺牲了一点即时性,但能避免用户填到一半的表单、正在支付的订单被强制刷新打断。 ## 更新链路应该怎么设计 浏览器会在页面导航、注册更新、事件触发以及大约 24 小时后检查 `sw.js` 是否变化。要注意,只有 Service Worker 文件本身或它 import 的脚本内容变化,才会触发更新流程;单纯改了被缓存的业务 JS,如果缓存策略不对,用户仍可能拿到旧资源。实践里建议把构建产物做文件名 hash,把缓存名跟版本绑定,并在主线程监听 `updatefound`。更新提示不要只写“刷新”,最好带上版本号和变更摘要,用户才知道是否值得马上中断当前操作。 ```js // main.js const reg = await navigator.serviceWorker.register('/sw.js'); reg.addEventListener('updatefound', () => { const worker = reg.installing; worker?.addEventListener('statechange', () => { if (worker.state === 'installed' && navigator.serviceWorker.controller) { showReloadTip(() => worker.postMessage({ type: 'SKIP_WAITING' })); } }); }); navigator.serviceWorker.addEventListener('controllerchange', () => location.reload()); ``` ```js // sw.js const CACHE = 'app-v2026-05-31'; self.addEventListener('message', e => { if (e.data?.type === 'SKIP_WAITING') self.skipWaiting(); }); self.addEventListener('activate', e => { e.waitUntil(caches.keys().then(keys => Promise.all( keys.filter(k => k.startsWith('app-') && k !== CACHE).map(k => caches.delete(k)) )).then(() => self.clients.claim())); }); ``` ## 缓存策略要和发布策略配套 如果前端使用 Vite、Webpack 或 Next.js,静态资源通常会带内容 hash,这类文件可以长期缓存;入口 HTML、manifest、`sw.js` 则应该保持较短缓存或直接走网络。服务端还要避免 CDN 把 `sw.js` 缓很久,否则浏览器检查到的永远是旧 Service Worker。一个常见配置是:`sw.js` 设置 `Cache-Control: no-cache`,hash 资源设置 `max-age=31536000, immutable`,HTML 设置 `no-cache` 或较短时间。这里的取舍很现实:资源缓存越激进,首屏越快;入口缓存越保守,更新越及时。 ## 追问 ### 为什么不直接在 install 里调用 skipWaiting? 可以,但只适合纯展示型页面或内部工具。`skipWaiting` 会让新 Service Worker 尽快接管旧页面,如果新旧资源协议不兼容,页面可能同时运行旧 JS 和新缓存规则。电商、表单、编辑器这类有未保存状态的页面,更适合弹出“有新版本,刷新后生效”。取舍点在于:即时修复 bug 和不中断用户操作,哪个风险更大。 ### HTML、JS、图片应该用同一种缓存策略吗? 不应该。HTML 决定入口和资源引用,通常用网络优先,否则用户可能一直进入旧版本;带 hash 的 JS/CSS 可以缓存优先,因为文件名变了就代表内容变了。图片、字体这类大资源可以 stale-while-revalidate,先快显示再后台更新。踩坑最多的是把 `index.html` 也长期缓存,结果线上发版后用户刷新十次还是旧入口。 ### 如何手动检查更新,避免等浏览器 24 小时? 可以在页面获得焦点、用户打开设置页、或后台定时调用 `registration.update()`。但不要每几秒轮询,Service Worker 更新检查本身也会产生请求,移动端弱网下体验很差。更常见的边界是:普通版本靠焦点触发检查,严重 bug 修复再配合服务端版本接口提示强刷新。这样既不浪费流量,也能覆盖用户长时间不关页面的情况。 ### 版本回滚应该怎么做? PWA 的回滚不是让浏览器“撤销 Service Worker”,而是重新部署一个旧逻辑版本并提升 `sw.js` 的内容版本。缓存清理时不要无脑删光所有缓存,可以保留最近一到两个版本,方便资源短暂回退。边界是安全事故或数据结构错误时,必须优先让 HTML 网络优先拿到修复入口,不能指望旧缓存自己恢复。线上还要记录 `controllerchange`、安装失败和缓存写入失败,方便判断是不是某个版本卡在 waiting。 ### 如何验证用户真的用上了新版本? 只看发布系统成功是不够的,前端应该上报当前应用版本、Service Worker 版本和缓存版本。可以在页面启动时读取构建时注入的版本号,再和服务端版本接口比对;如果差距太大,再提示用户刷新或清理缓存。注意不要把版本检查做成阻塞启动,否则版本接口挂了会拖垮整个应用。更稳的做法是先让用户进入页面,再异步判断是否需要更新。 ## 收束 PWA 更新体验的关键,是让“快”和“稳”保持平衡。静态资源靠 hash 和长期缓存换速度,入口和 Service Worker 靠谨慎缓存换可控更新;小版本可以温和提示,大故障才需要强制刷新。只要把生命周期、缓存头、用户提示和版本监控串起来,PWA 就能做到接近原生应用的更新体验,同时保留 Web 快速发布的优势。
服务端5月31日 17:42
PWA 开发常用哪些工具和框架?怎么选才不踩坑?PWA 开发不是装一个插件就结束,它同时涉及 Manifest、Service Worker、缓存策略、离线体验、安装提示、性能审计和部署缓存头。常用工具很多,但真正核心的只有几类:生成和维护 Service Worker 的 Workbox,做质量审计的 Lighthouse,承载应用结构的 React、Vue、Angular 或 Vite,以及用于端到端验证的浏览器测试工具。选工具时要先确定应用类型,是内容站、后台系统、离线表单,还是接近原生体验的移动 Web。 ## 追问 ### Workbox 适合所有 PWA 吗? Workbox 适合大多数需要预缓存静态资源、运行时缓存图片或 API 的 PWA,因为它把 Service Worker 里容易写错的细节封装好了。取舍是你要理解缓存策略,而不是只复制配置;`CacheFirst`、`NetworkFirst` 和 `StaleWhileRevalidate` 对用户体验的影响完全不同。边界是对非常特殊的离线协作、复杂同步队列或加密缓存,手写 Service Worker 可能更可控。常见坑是把 HTML 也长期 CacheFirst,结果用户一直看旧版本,发布后像“没上线”。 ```js import { registerRoute } from "workbox-routing"; import { NetworkFirst, CacheFirst } from "workbox-strategies"; registerRoute(({ request }) => request.mode === "navigate", new NetworkFirst()); registerRoute(({ request }) => request.destination === "image", new CacheFirst()); ``` ### React、Vue、Angular 做 PWA 有什么区别? 三者都能做 PWA,差别主要在工程体系和团队熟悉度。React 和 Vue 通常会配合 Vite、vite-plugin-pwa 或框架自带方案,配置灵活,上手成本低;Angular 的 `@angular/pwa` 集成度高,适合 Angular 体系内的企业项目。取舍是插件越自动,越容易让人忽略 Service Worker 的真实行为,出了缓存问题反而不知道从哪里查。踩坑点是本地 dev 环境通常不完整模拟生产缓存,必须用 production build 验证安装和离线行为。 ```bash npm create vite@latest my-pwa npm i vite-plugin-pwa -D npm run build && npm run preview ``` ### Lighthouse 分数能代表 PWA 质量吗? Lighthouse 很适合做第一轮体检,它能检查 Manifest、Service Worker、HTTPS、性能和可访问性等基础问题。取舍是分数高不等于真实体验好,尤其是弱网、离线提交、登录过期和缓存更新这些场景,自动审计覆盖有限。边界是 Lighthouse 更像门槛检查,不是产品验收标准。常见坑是只盯着 PWA badge 或性能分,忽略了用户离线后看到的错误提示是否可理解。 ```bash npm i -g lighthouse lighthouse https://example.com --view ``` ### PWA 测试应该测哪些关键路径? 至少要测首次访问、刷新、离线打开、重新联网、版本更新和安装入口这几条路径。内容站更关注静态资源和页面缓存,业务系统更关注接口失败、草稿保存和权限过期。取舍是全量自动化成本较高,可以先把 Service Worker 注册、缓存命中和离线页放进 Playwright 或 Cypress 的冒烟测试。踩坑是只在 Chrome 桌面测通过就上线,移动端浏览器、iOS 安装限制和存储清理策略都可能让体验变样。 ```js // Playwright 思路:构建后访问,再模拟离线 await page.goto("https://example.com"); await context.setOffline(true); await page.reload(); ``` ### 部署 PWA 最容易忽略什么? 最容易忽略的是缓存头和 Service Worker 作用域。`sw.js` 通常不应该被 CDN 长期强缓存,否则新版本发布后用户拿不到新的 Service Worker;静态 hash 资源则可以长缓存,提高重复访问速度。取舍是缓存越激进,性能越好,但回滚和热修复越麻烦,所以 HTML、Service Worker、API 响应和静态资源要分开设置策略。常见坑是把应用部署在子路径,却没有处理 scope,导致 Service Worker 控制不到你以为能控制的页面。 ```nginx location = /sw.js { add_header Cache-Control "no-cache"; } location /assets/ { add_header Cache-Control "public, max-age=31536000, immutable"; } ``` ## 小结 PWA 工具选择可以很朴素:Workbox 负责缓存和 Service Worker,Lighthouse 负责基础审计,框架插件负责构建集成,浏览器自动化负责关键路径验证。真正决定质量的不是用了多少工具,而是有没有把离线、更新、缓存和部署边界测清楚。
前端2024年7月17日 22:07
如何提高PWA的性能?提高PWA(Progressive Web App)的性能可以从以下几个方面进行: 1. **服务工作器优化**:服务工作器(Service Worker)是PWA的核心,负责资源的缓存和离线功能。合理设置缓存策略,如使用缓存优先(cache-first)策略对静态资源进行缓存,对API请求使用网络优先(network-first)策略以确保数据的实时性。 2. **懒加载**:实现图片、视频或长列表的懒加载,只在用户滚动到它们时才加载这些资源。这可以显著减少初次加载页面时的数据传输量,并提高页面响应速度。 3. **Minify 和压缩资源**:使用工具如Webpack或者Gulp来压缩JavaScript、CSS和HTML文件,减少文件体积,加快加载速度。 4. **使用HTTP/2**:HTTP/2 提供了头部压缩、服务器推送等功能,可以减少延迟,提高加载效率。确保服务器支持HTTP/2可以显著提升资源加载速度。 5. **优化图片**:对图片进行格式优化(如使用WebP格式),并根据设备进行适当的尺寸调整,减少不必要的数据加载。 6. **使用Web Assembly**:对性能要求极高的任务,可以考虑使用Web Assembly来提高执行效率,特别是在处理图形渲染或视频编解码等场景。 7. **动态导入**:对JavaScript模块使用动态导入(Dynamic Imports),按需加载模块,减少不必要的JavaScript加载和解析时间。 8. **预加载和预获取**:使用 `<link rel="preload">` 和 `<link rel="prefetch">` 为未来的导航请求资源,可以提前加载关键资源。 9. **优化CSS和JavaScript执行性能**:减少重绘(Repaints)和回流(Reflows)的产生,优化动画的性能,避免长时间运行的JavaScript任务阻塞主线程。 通过这些具体的技术策略和方法,可以有效提高PWA的性能,提升用户体验。