PWA 离线优先策略——提升用户体验的关键步骤

0 阅读3分钟

渐进式 Web 应用程序 (PWA) 使用服务工作者和缓存 API 实现离线优先策略,无需互联网连接即可访问网站的部分或全部内容。

  1. 创建服务工作线程文件(service-worker.js) self.addEventListener('install', (event) => { event.waitUntil( caches.open('my-cache-v1').then((cache) => { return cache.addAll([ '/index.html', '/style.css', '/script.js', // Add other files to precache ]); }) ); });

self.addEventListener('fetch', (event) => { event.respondWith( caches.match(event.request).then((response) => { if (response) { return response; } return fetch(event.request).then((networkResponse) => { caches.open('my-cache-v1').then((cache) => { cache.put(event.request.url, networkResponse.clone()); }); return networkResponse; }).catch(() => { // Return a fallback response, like an error page, if all attempts fail return caches.match('/offline.html'); }); }) ); }); 2. 注册 Service Worker 在您的主应用程序中注册 Service Worker:

if ('serviceWorker' in navigator) { window.addEventListener('load', () => { navigator.serviceWorker .register('/service-worker.js') .then((registration) => { console.log('Service Worker registered:', registration); }) .catch((error) => { console.error('Service Worker registration failed:', error); }); }); } 3.更新策略 当有新的应用程序版本可用时,通过监听activate事件来更新 Service Worker 和缓存内容:

self.addEventListener('activate', (event) => { event.waitUntil( caches.keys().then((cacheNames) => { return Promise.all( cacheNames.filter((cacheName) => cacheName !== 'my-cache-v1').map((cacheName) => caches.delete(cacheName)) ); }) ); }); 4. 更新 Service Worker 要更新 Service Worker,请更改其文件名(例如,添加版本号)。这会通知浏览器将其视为新的 Service Worker,从而触发安装过程。

  1. 管理 Service Worker 生命周期 通过允许旧 Service Worker 在关闭之前完成请求,确保更新 Service Worker 不会破坏用户体验:

self.addEventListener('message', (event) => { if (event.data && event.data.type === 'SKIP_WAITING') { self.skipWaiting(); } }); 6.配置清单文件 创建一个manifest.json文件来定义应用程序元数据和离线图标:

{ "short_name": "My App", "name": "My Awesome Progressive Web App", "icons": [ { "src": "icon-192.png", "sizes": "192x192", "type": "image/png" }, { "src": "icon-512.png", "sizes": "512x512", "type": "image/png" } ], "start_url": "/index.html", "display": "standalone", "background_color": "#ffffff", "theme_color": "#000000" } 在 HTML 中引用清单

7.离线通知和重新加载提示 当用户恢复连接时通知用户重新加载页面以获取更新的内容:

self.addEventListener('online', (event) => { clients.matchAll({ type: 'window' }).then((clients) => { clients.forEach((client) => { client.postMessage({ type: 'RELOAD' }); }); }); });

self.addEventListener('message', (event) => { if (event.data && event.data.type === 'RELOAD') { clients.matchAll({ type: 'window' }).then((clients) => { clients.forEach((client) => { if (client.url === self.registration.scope && 'focus' in client) { client.focus(); client.reload(); } }); }); } }); 在主应用程序中收听消息 navigator.serviceWorker.addEventListener('message', (event) => { if (event.data && event.data.type === 'RELOAD') { alert('Network restored, refresh the page for the latest content.'); location.reload(); } }); 8. 线下提示和体验 当用户离线时提供友好的离线页面或提示:

You're Offline

Please try again later.

在 Service Worker 的fetch事件中处理此问题:

self.addEventListener('fetch', (event) => { event.respondWith( caches.match(event.request).then((response) => { if (response) { return response; } // Handle failed network requests return fetch(event.request).catch(() => { // Return offline page return caches.match('/offline.html'); }); }) ); }); 9.更新缓存策略 要缓存特定的资源版本而不是始终使用最新版本,请在 Service Worker 中实现版本控制:

const CACHE_NAME = 'my-cache-v2'; const urlsToCache = [ // ... ];

self.addEventListener('install', (event) => { event.waitUntil( caches.open(CACHE_NAME).then((cache) => { return cache.addAll(urlsToCache); }) ); });

self.addEventListener('fetch', (event) => { event.respondWith( caches.match(event.request).then((response) => { if (response) { return response; } return fetch(event.request).then((networkResponse) => { caches.open(CACHE_NAME).then((cache) => { cache.put(event.request.url, networkResponse.clone()); }); return networkResponse; }); }) ); }); 10. 使用 App Shell 架构 App Shell 模型是一种常见的 PWA 设计模式,它提供了一个基本的 UI 框架,即使在离线状态下也可以加载。它通常包含非动态内容,例如导航、标题和侧边栏。

创建具有基本布局和样式的 App Shell HTML 文件(例如app-shell.html),然后在 Service Worker 中预缓存它:

const appShellUrls = [ '/app-shell.html', '/app-style.css', // Other App Shell-related resources ];

self.addEventListener('install', (event) => { event.waitUntil( caches.open('app-shell-cache').then((cache) => { return cache.addAll(appShellUrls); }) ); }); 在以下情况下优先从缓存中获取 App Shell 资源fetch:

self.addEventListener('fetch', (event) => { if (event.request.mode === 'navigate') { event.respondWith(caches.match('/app-shell.html')); } else { event.respondWith( caches.match(event.request).then((response) => { if (response) { return response; } return fetch(event.request); }) ); } }); 11. 使用 Service Worker 拦截网络请求 Service Worker 可以拦截特定的网络请求(例如 API 调用),以离线返回默认或缓存的响应,从而实现一致的用户体验:

self.addEventListener('fetch', (event) => { if (event.request.url.startsWith('')) { event.respondWith( caches.match(event.request).then((response) => { if (response) { return response; } return fetch(event.request).then((networkResponse) => { caches.open('api-cache').then((cache) => { cache.put(event.request.url, networkResponse.clone()); }); return networkResponse; }); }) ); } else { // Handle other non-API requests } }); 12.集成WebSocket支持 对于使用 WebSockets 进行实时通信的应用程序,请使用该workbox-websocket库管理 Service Worker 中的 WebSocket 连接,确保消息离线处理:

workbox.webSocket.register('www.mytiesarongs.com', { onConnect: (client) => { console.log('WebSocket connected:', client); }, onClose: (client) => { console.log('WebSocket disconnected:', client); }, }); 13. 测试和监控 使用 Chrome DevTools 的网络模拟功能,在各种网络条件下(包括 2G、3G 和离线)测试您的 PWA。定期使用 Lighthouse 等工具评估性能和离线体验。

14.总结 这些策略能够创建高可用性的 PWA,即使在离线或弱网络条件下也能提供出色的用户体验。PWA 的目标是提供接近原生的应用体验,因此持续的优化和测试至关重要。访问了解更多相关代码资讯:www.ysdslt.com