乐闻世界logo
搜索文章和话题

如何实现 PWA 的离线功能?需要哪些关键步骤?

2月18日 21:53

PWA 的离线功能是其核心特性之一,主要通过 Service Worker 和缓存机制实现。以下是实现离线功能的完整方案:

离线功能的核心组件

1. Service Worker

Service Worker 是离线功能的基础,它能够拦截网络请求并从缓存中返回资源。

2. Cache API

用于存储和管理缓存资源。

实现离线功能的步骤

步骤 1:注册 Service Worker

javascript
// 在主线程中注册 if ('serviceWorker' in navigator) { window.addEventListener('load', () => { navigator.serviceWorker.register('/sw.js') .then(registration => { console.log('SW registered:', registration); }) .catch(error => { console.log('SW registration failed:', error); }); }); }

步骤 2:预缓存关键资源

javascript
// sw.js const CACHE_NAME = 'my-pwa-v1'; const ASSETS_TO_CACHE = [ '/', '/index.html', '/styles/main.css', '/scripts/app.js', '/images/logo.png', '/offline.html' ]; self.addEventListener('install', event => { event.waitUntil( caches.open(CACHE_NAME) .then(cache => { return cache.addAll(ASSETS_TO_CACHE); }) .then(() => { return self.skipWaiting(); }) ); });

步骤 3:实现缓存策略

javascript
self.addEventListener('fetch', event => { event.respondWith( caches.match(event.request) .then(response => { // 缓存命中,直接返回 if (response) { return response; } // 缓存未命中,请求网络 return fetch(event.request) .then(response => { // 检查响应是否有效 if (!response || response.status !== 200 || response.type !== 'basic') { return response; } // 克隆响应并缓存 const responseToCache = response.clone(); caches.open(CACHE_NAME) .then(cache => { cache.put(event.request, responseToCache); }); return response; }) .catch(() => { // 网络请求失败,返回离线页面 if (event.request.mode === 'navigate') { return caches.match('/offline.html'); } }); }) ); });

步骤 4:创建离线页面

html
<!-- offline.html --> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>离线</title> <style> body { display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; font-family: Arial, sans-serif; background: #f5f5f5; } .offline-container { text-align: center; padding: 40px; background: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); } .offline-icon { font-size: 64px; margin-bottom: 20px; } .offline-title { font-size: 24px; margin-bottom: 10px; color: #333; } .offline-message { color: #666; margin-bottom: 20px; } .retry-button { padding: 10px 20px; background: #007bff; color: white; border: none; border-radius: 4px; cursor: pointer; } </style> </head> <body> <div class="offline-container"> <div class="offline-icon">📡</div> <h1 class="offline-title">您当前处于离线状态</h1> <p class="offline-message">请检查您的网络连接后重试</p> <button class="retry-button" onclick="window.location.reload()">重新加载</button> </div> <script> // 监听网络状态变化 window.addEventListener('online', () => { window.location.reload(); }); </script> </body> </html>

步骤 5:监听网络状态

javascript
// 在主线程中监听网络状态 window.addEventListener('online', () => { console.log('网络已连接'); // 可以在这里执行一些操作,比如同步数据 }); window.addEventListener('offline', () => { console.log('网络已断开'); // 显示离线提示 showOfflineNotification(); }); function showOfflineNotification() { const notification = document.createElement('div'); notification.textContent = '您当前处于离线状态'; notification.style.cssText = ` position: fixed; top: 20px; left: 50%; transform: translateX(-50%); background: #ff9800; color: white; padding: 10px 20px; border-radius: 4px; z-index: 9999; `; document.body.appendChild(notification); setTimeout(() => { notification.remove(); }, 3000); }

高级离线功能

1. Background Sync(后台同步)

javascript
// 注册同步事件 self.addEventListener('sync', event => { if (event.tag === 'sync-data') { event.waitUntil(syncData()); } }); async function syncData() { // 获取离线时存储的数据 const offlineData = await getOfflineData(); // 同步到服务器 for (const data of offlineData) { try { await fetch('/api/sync', { method: 'POST', body: JSON.stringify(data) }); // 同步成功,删除本地数据 await removeOfflineData(data.id); } catch (error) { console.error('同步失败:', error); } } } // 在主线程中请求同步 navigator.serviceWorker.ready.then(registration => { registration.sync.register('sync-data'); });

2. IndexedDB 存储离线数据

javascript
// 打开 IndexedDB const dbPromise = idb.open('my-pwa-db', 1, upgradeDB => { upgradeDB.createObjectStore('offline-data', { keyPath: 'id' }); }); // 保存离线数据 async function saveOfflineData(data) { const db = await dbPromise; await db.add('offline-data', data); } // 获取离线数据 async function getOfflineData() { const db = await dbPromise; return await db.getAll('offline-data'); } // 删除离线数据 async function removeOfflineData(id) { const db = await dbPromise; await db.delete('offline-data', id); }

离线功能的最佳实践

  1. 预缓存关键资源:确保核心功能离线可用
  2. 提供友好的离线页面:告知用户当前状态并提供解决方案
  3. 监听网络状态:及时响应网络变化
  4. 实现数据同步:离线时存储数据,在线时同步
  5. 设置合理的缓存策略:平衡性能和新鲜度
  6. 测试离线场景:使用 Chrome DevTools 的 Offline 模式测试
  7. 提供网络状态指示器:让用户了解当前网络状态

测试离线功能

使用 Chrome DevTools 测试:

  1. 打开 DevTools(F12)
  2. 切换到 Network 标签
  3. 勾选 "Offline" 模式
  4. 刷新页面,测试离线功能
  5. 取消勾选 "Offline",测试恢复功能
标签:PWA