您现在的位置是:网站首页 > 渐进式Web应用(PWA)的特殊模式需求文章详情
渐进式Web应用(PWA)的特殊模式需求
陈川
【
JavaScript
】
13493人已围观
10182字
渐进式Web应用(PWA)的特殊模式需求
渐进式Web应用(PWA)结合了Web和原生应用的优点,但在实现过程中需要特定的设计模式来应对其独特需求。离线优先、后台同步、缓存策略等问题都需要通过模式化解决方案来处理。
离线优先模式
PWA的核心特性之一是离线工作能力,这要求应用采用"离线优先"的设计哲学。Service Worker作为关键技术,需要配合Cache API实现资源缓存。
// 注册Service Worker
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js')
.then(registration => {
console.log('SW registered');
});
}
// sw.js中的缓存策略
const CACHE_NAME = 'v1';
const urlsToCache = [
'/',
'/styles/main.css',
'/scripts/app.js'
];
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => cache.addAll(urlsToCache))
);
});
这种模式需要考虑缓存版本控制、缓存清理策略等问题。常见的做法是在activate事件中清理旧缓存:
self.addEventListener('activate', event => {
const cacheWhitelist = [CACHE_NAME];
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.map(cacheName => {
if (!cacheWhitelist.includes(cacheName)) {
return caches.delete(cacheName);
}
})
);
})
);
});
后台同步模式
PWA需要处理网络连接不稳定的情况,后台同步模式允许应用在网络恢复后完成未成功的操作。这特别适合表单提交等关键操作。
// 注册后台同步
navigator.serviceWorker.ready.then(registration => {
return registration.sync.register('submit-form');
});
// Service Worker中处理同步事件
self.addEventListener('sync', event => {
if (event.tag === 'submit-form') {
event.waitUntil(
submitFormData()
.then(() => {
return showNotification('数据已同步');
})
);
}
});
async function submitFormData() {
const requests = await getPendingRequests();
return Promise.all(requests.map(request => {
return fetch(request.url, request.options)
.then(response => {
if (response.ok) {
return deletePendingRequest(request.id);
}
throw new Error('同步失败');
});
}));
}
应用外壳架构
应用外壳(Application Shell)是PWA性能优化的关键模式,它通过缓存UI骨架实现快速加载。
// 缓存应用外壳
const SHELL_CACHE = 'shell-v1';
const SHELL_URLS = [
'/',
'/index.html',
'/css/shell.css',
'/js/shell.js',
'/images/logo.png'
];
self.addEventListener('install', event => {
event.waitUntil(
caches.open(SHELL_CACHE)
.then(cache => cache.addAll(SHELL_URLS))
);
});
// 网络优先回退到缓存的策略
self.addEventListener('fetch', event => {
event.respondWith(
fetch(event.request)
.catch(() => caches.match(event.request))
);
});
状态持久化模式
PWA需要处理应用状态持久化问题,特别是在多标签页或重新加载时保持状态一致。
// 使用IndexedDB存储应用状态
function saveAppState(state) {
return new Promise((resolve, reject) => {
const request = indexedDB.open('AppStateDB', 1);
request.onupgradeneeded = (event) => {
const db = event.target.result;
if (!db.objectStoreNames.contains('state')) {
db.createObjectStore('state', { keyPath: 'id' });
}
};
request.onsuccess = (event) => {
const db = event.target.result;
const transaction = db.transaction('state', 'readwrite');
const store = transaction.objectStore('state');
store.put({ id: 'current', data: state });
resolve();
};
request.onerror = (event) => {
reject(event.target.error);
};
});
}
// 跨标签页状态同步
const broadcast = new BroadcastChannel('app-state');
broadcast.addEventListener('message', (event) => {
if (event.data.type === 'STATE_UPDATE') {
updateUI(event.data.state);
}
});
function notifyStateChange(state) {
broadcast.postMessage({
type: 'STATE_UPDATE',
state: state
});
}
推送通知模式
推送通知是PWA的重要特性,需要处理用户权限、消息显示和点击行为。
// 请求通知权限
function requestNotificationPermission() {
return new Promise((resolve, reject) => {
if (!('Notification' in window)) {
reject(new Error('通知API不支持'));
return;
}
if (Notification.permission === 'granted') {
resolve(true);
} else if (Notification.permission !== 'denied') {
Notification.requestPermission().then(permission => {
resolve(permission === 'granted');
});
} else {
resolve(false);
}
});
}
// 显示通知
function showNotification(title, options) {
if (Notification.permission === 'granted') {
navigator.serviceWorker.ready.then(registration => {
registration.showNotification(title, options);
});
}
}
// Service Worker中处理通知点击
self.addEventListener('notificationclick', event => {
event.notification.close();
event.waitUntil(
clients.matchAll({ type: 'window' })
.then(clientList => {
if (clientList.length > 0) {
return clientList[0].focus();
}
return clients.openWindow('/');
})
);
});
数据预取模式
预取关键数据可以显著提升PWA的用户体验,特别是在预测用户行为方面。
// 基于用户行为预测预取数据
const PREDICTIVE_PREFETCH = {
'/products': '/api/products?limit=5',
'/profile': '/api/user/profile'
};
function prefetchData() {
const path = window.location.pathname;
if (PREDICTIVE_PREFETCH[path]) {
fetch(PREDICTIVE_PREFETCH[path])
.then(response => response.json())
.then(data => {
// 将数据存储在Cache API或IndexedDB中
cacheData(data);
});
}
}
// 监听可能触发预取的鼠标悬停事件
document.addEventListener('DOMContentLoaded', () => {
const links = document.querySelectorAll('a[data-prefetch]');
links.forEach(link => {
link.addEventListener('mouseenter', () => {
const url = link.getAttribute('href');
prefetchResource(url);
}, { once: true });
});
});
function prefetchResource(url) {
fetch(url, { mode: 'no-cors' })
.then(() => console.log(`预取完成: ${url}`));
}
渐进增强模式
PWA需要确保在旧浏览器或功能受限环境中仍能提供基本功能。
// 功能检测提供回退方案
function initApp() {
if ('serviceWorker' in navigator && 'caches' in window) {
initPWA();
} else {
initLegacyApp();
}
}
function initPWA() {
// 完整的PWA功能
registerServiceWorker();
setupOfflineMode();
enablePushNotifications();
}
function initLegacyApp() {
// 基本功能
setupBasicNavigation();
showLegacyWarning();
}
// 检测网络状态
function setupOnlineOfflineHandlers() {
window.addEventListener('online', updateOnlineStatus);
window.addEventListener('offline', updateOnlineStatus);
function updateOnlineStatus() {
if (navigator.onLine) {
document.body.classList.remove('offline');
syncPendingChanges();
} else {
document.body.classList.add('offline');
showOfflineUI();
}
}
}
性能优化模式
PWA需要特别关注性能优化,特别是首屏加载时间和交互响应速度。
// 使用Web Worker处理复杂计算
const worker = new Worker('compute.worker.js');
worker.onmessage = (event) => {
updateUIWithResults(event.data);
};
function startHeavyComputation(data) {
worker.postMessage(data);
}
// 延迟加载非关键资源
function loadLazyResources() {
const lazyImages = [].slice.call(document.querySelectorAll('img.lazy'));
if ('IntersectionObserver' in window) {
const lazyImageObserver = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
const lazyImage = entry.target;
lazyImage.src = lazyImage.dataset.src;
lazyImage.classList.remove('lazy');
lazyImageObserver.unobserve(lazyImage);
}
});
});
lazyImages.forEach((lazyImage) => {
lazyImageObserver.observe(lazyImage);
});
} else {
// 回退方案
lazyImages.forEach((lazyImage) => {
lazyImage.src = lazyImage.dataset.src;
});
}
}
// 关键CSS内联
function inlineCriticalCSS() {
const criticalCSS = `
/* 首屏关键样式 */
body { font-family: sans-serif; }
.header { background: #333; color: white; }
/* 其他关键样式 */
`;
const style = document.createElement('style');
style.textContent = criticalCSS;
document.head.appendChild(style);
// 异步加载完整CSS
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = '/styles/main.css';
document.head.appendChild(link);
}
安全考虑模式
PWA需要特别注意安全问题,包括HTTPS要求、内容安全策略等。
// 内容安全策略(CSP)设置
// 在HTTP头或meta标签中设置
// <meta http-equiv="Content-Security-Policy" content="default-src 'self' https:; script-src 'self' 'unsafe-inline'">
// Service Worker安全实践
self.addEventListener('fetch', event => {
const url = new URL(event.request.url);
// 只允许同源请求或特定白名单
if (url.origin !== location.origin && !ALLOWED_ORIGINS.includes(url.origin)) {
event.respondWith(new Response('禁止跨域请求', { status: 403 }));
return;
}
// 防止XSS攻击
if (event.request.mode === 'navigate' &&
event.request.method === 'GET' &&
event.request.headers.get('accept').includes('text/html')) {
event.respondWith(
fetch(event.request)
.then(response => {
if (response.status === 200 &&
!response.headers.get('content-type').includes('text/html')) {
return new Response('', { status: 404 });
}
return response;
})
);
return;
}
// 正常处理其他请求
event.respondWith(
caches.match(event.request)
.then(response => response || fetch(event.request))
);
});
更新管理模式
PWA需要妥善处理应用更新,确保用户获得最新版本同时不中断体验。
// 检测更新并提示用户
navigator.serviceWorker.register('/sw.js').then(registration => {
registration.addEventListener('updatefound', () => {
const newWorker = registration.installing;
newWorker.addEventListener('statechange', () => {
if (newWorker.state === 'installed') {
if (navigator.serviceWorker.controller) {
// 新内容可用,显示更新提示
showUpdateAlert(() => {
newWorker.postMessage({ action: 'skipWaiting' });
});
}
}
});
});
});
// Service Worker中处理跳过等待
self.addEventListener('message', event => {
if (event.data.action === 'skipWaiting') {
self.skipWaiting();
}
});
// 控制页面刷新
let refreshing;
navigator.serviceWorker.addEventListener('controllerchange', () => {
if (refreshing) return;
refreshing = true;
window.location.reload();
});
// 版本检查
const APP_VERSION = '1.2.0';
const versionCheck = () => {
fetch('/version.json')
.then(response => response.json())
.then(data => {
if (data.version !== APP_VERSION) {
showUpdateAvailable();
}
});
};
// 定期检查
setInterval(versionCheck, 3600000); // 每小时检查一次
上一篇: 全栈开发方案