PWA与离线Web应用开发1. 技术分析1.1 PWA概述PWA渐进式Web应用提供原生应用体验PWA特性 离线支持: Service Worker缓存 推送通知: 类似原生应用 安装到主屏幕: 无需应用商店 响应式设计: 适配各种设备 核心技术: Service Worker Web App Manifest Push API1.2 Service WorkerService Worker特点 独立线程: 不阻塞主线程 事件驱动: 监听fetch、push等事件 离线缓存: 拦截网络请求 生命周期: 安装 - 激活 - 等待 - 激活1.3 Web App ManifestManifest配置 名称和描述 图标配置 启动URL 显示模式 显示模式: fullscreen: 全屏 standalone: 独立应用 minimal-ui: 最小UI browser: 浏览器模式2. 核心功能实现2.1 Service Worker配置// sw.js const CACHE_NAME my-app-cache-v1; const ASSETS_TO_CACHE [ /, /index.html, /styles.css, /app.js, /offline.html ]; // 安装阶段 self.addEventListener(install, (event) { event.waitUntil( caches.open(CACHE_NAME).then((cache) { return cache.addAll(ASSETS_TO_CACHE); }).then(() { self.skipWaiting(); }) ); }); // 激活阶段 self.addEventListener(activate, (event) { event.waitUntil( caches.keys().then((cacheNames) { return Promise.all( cacheNames.filter((name) { return name ! CACHE_NAME; }).map((name) { return caches.delete(name); }) ); }).then(() { return self.clients.claim(); }) ); }); // 拦截网络请求 self.addEventListener(fetch, (event) { event.respondWith( caches.match(event.request).then((cachedResponse) { if (cachedResponse) { return cachedResponse; } return fetch(event.request).then((networkResponse) { if (!networkResponse || networkResponse.status ! 200) { return caches.match(/offline.html); } const responseToCache networkResponse.clone(); caches.open(CACHE_NAME).then((cache) { cache.put(event.request, responseToCache); }); return networkResponse; }).catch(() { return caches.match(/offline.html); }); }) ); });2.2 Web App Manifest{ name: My PWA App, short_name: My App, description: A Progressive Web Application, start_url: /, display: standalone, background_color: #ffffff, theme_color: #3498db, orientation: portrait-primary, icons: [ { src: icon-192x192.png, sizes: 192x192, type: image/png, purpose: any maskable }, { src: icon-512x512.png, sizes: 512x512, type: image/png, purpose: any maskable } ], splash_pages: null }2.3 推送通知// 注册推送服务 async function registerPush() { if (serviceWorker in navigator PushManager in window) { const registration await navigator.serviceWorker.ready; const subscription await registration.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: urlBase64ToUint8Array( YOUR_PUBLIC_KEY ) }); await fetch(/api/subscribe, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify(subscription) }); } } // 监听推送事件 self.addEventListener(push, (event) { const data event.data?.json() || { title: New notification }; const options { body: data.body, icon: icon-192x192.png, badge: badge.png, data: { url: data.url } }; event.waitUntil( self.registration.showNotification(data.title, options) ); }); // 点击通知 self.addEventListener(notificationclick, (event) { event.notification.close(); event.waitUntil( clients.openWindow(event.notification.data.url) ); });3. 性能对比3.1 PWA vs Native App特性PWANative App安装方式网页安装应用商店更新方式自动更新手动更新离线支持Service Worker内置缓存推送通知Push API系统通知3.2 缓存策略对比策略适用场景实现复杂度Cache First静态资源低Network First动态内容中Stale-While-Revalidate频繁更新内容中3.3 存储方案对比方案存储限制持久化适用场景localStorage5MB是小数据IndexedDB无限制是大数据Cache API无限制是资源缓存4. 最佳实践4.1 安装提示let deferredPrompt; window.addEventListener(beforeinstallprompt, (event) { event.preventDefault(); deferredPrompt event; showInstallButton(); }); function showInstallButton() { const installBtn document.getElementById(install-btn); installBtn.style.display block; installBtn.addEventListener(click, async () { if (deferredPrompt) { deferredPrompt.prompt(); const { outcome } await deferredPrompt.userChoice; if (outcome accepted) { console.log(User accepted the install prompt); } deferredPrompt null; installBtn.style.display none; } }); }4.2 后台同步// 注册后台同步 async function syncData() { if (SyncManager in window) { const registration await navigator.serviceWorker.ready; try { await registration.sync.register(sync-data); console.log(Sync registered); } catch (err) { console.log(Sync failed:, err); } } } // Service Worker中处理同步 self.addEventListener(sync, (event) { if (event.tag sync-data) { event.waitUntil(syncPendingData()); } }); async function syncPendingData() { const pendingData await getPendingData(); for (const item of pendingData) { await fetch(/api/sync, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify(item) }); await removePendingItem(item.id); } }5. 总结PWA提供了接近原生应用的体验Service Worker离线支持和缓存Web App Manifest安装到主屏幕Push API推送通知后台同步离线数据同步对比数据如下PWA无需应用商店即可安装Service Worker提供离线支持Cache API适合资源缓存IndexedDB适合大数据存储PWA是未来Web应用的发展方向推荐在项目中采用。