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

如何优化 PWA 的性能?有哪些关键的性能优化策略?

2月18日 21:53

PWA 的性能优化对于提供良好的用户体验至关重要。以下是全面的性能优化策略:

1. 资源加载优化

预缓存关键资源

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

懒加载非关键资源

javascript
// 图片懒加载 const images = document.querySelectorAll('img[data-src]'); const imageObserver = new IntersectionObserver((entries, observer) => { entries.forEach(entry => { if (entry.isIntersecting) { const img = entry.target; img.src = img.dataset.src; img.removeAttribute('data-src'); observer.unobserve(img); } }); }); images.forEach(img => imageObserver.observe(img)); // 组件懒加载 const LazyComponent = React.lazy(() => import('./LazyComponent'));

代码分割

javascript
// 使用动态 import 进行代码分割 async function loadFeature() { const module = await import('./feature.js'); module.init(); } // React 中的代码分割 const Home = React.lazy(() => import('./Home')); const About = React.lazy(() => import('./About'));

2. 缓存策略优化

智能缓存策略

javascript
self.addEventListener('fetch', event => { const url = new URL(event.request.url); // 静态资源:缓存优先 if (url.pathname.match(/\.(css|js|png|jpg|jpeg|gif|svg|woff|woff2)$/)) { event.respondWith(cacheFirst(event.request)); } // API 请求:网络优先 else if (url.pathname.startsWith('/api/')) { event.respondWith(networkFirst(event.request)); } // HTML 文档:网络优先,失败时返回缓存 else if (event.request.mode === 'navigate') { event.respondWith(networkFirstWithFallback(event.request)); } // 其他:缓存同时更新 else { event.respondWith(staleWhileRevalidate(event.request)); } }); function cacheFirst(request) { return caches.match(request).then(response => { return response || fetch(request).then(networkResponse => { const responseClone = networkResponse.clone(); caches.open('static-cache').then(cache => { cache.put(request, responseClone); }); return networkResponse; }); }); } function networkFirst(request) { return fetch(request).then(networkResponse => { const responseClone = networkResponse.clone(); caches.open('dynamic-cache').then(cache => { cache.put(request, responseClone); }); return networkResponse; }).catch(() => caches.match(request)); } function staleWhileRevalidate(request) { return caches.match(request).then(cachedResponse => { const fetchPromise = fetch(request).then(networkResponse => { caches.open('dynamic-cache').then(cache => { cache.put(request, networkResponse.clone()); }); return networkResponse; }); return cachedResponse || fetchPromise; }); }

缓存版本管理

javascript
const CACHE_VERSION = 'v2'; const CACHE_NAME = `my-pwa-${CACHE_VERSION}`; self.addEventListener('activate', event => { event.waitUntil( caches.keys().then(cacheNames => { return Promise.all( cacheNames.map(cacheName => { if (cacheName !== CACHE_NAME && cacheName.startsWith('my-pwa-')) { return caches.delete(cacheName); } }) ); }).then(() => self.clients.claim()) ); });

3. 图片优化

使用现代图片格式

html
<picture> <source srcset="image.webp" type="image/webp"> <source srcset="image.jpg" type="image/jpeg"> <img src="image.jpg" alt="Description" loading="lazy"> </picture>

响应式图片

html
<img src="image-small.jpg" srcset="image-small.jpg 480w, image-medium.jpg 768w, image-large.jpg 1024w" sizes="(max-width: 480px) 480px, (max-width: 768px) 768px, 1024px" alt="Description" loading="lazy" >

图片压缩和优化

javascript
// 使用 sharp 库压缩图片 const sharp = require('sharp'); async function optimizeImage(inputPath, outputPath) { await sharp(inputPath) .resize(800, 600, { fit: 'inside' }) .webp({ quality: 80 }) .toFile(outputPath); }

4. JavaScript 优化

减少包体积

javascript
// 使用 Tree Shaking // 只导入需要的函数 import { debounce } from 'lodash-es'; // 避免导入整个库 // import _ from 'lodash'; // ❌ 避免

使用 Web Workers

javascript
// 主线程 const worker = new Worker('worker.js'); worker.postMessage({ data: largeDataSet }); worker.onmessage = (event) => { console.log('Processed data:', event.data); }; // worker.js self.onmessage = (event) => { const result = processData(event.data.data); self.postMessage(result); };

防抖和节流

javascript
// 防抖 function debounce(func, wait) { let timeout; return function(...args) { clearTimeout(timeout); timeout = setTimeout(() => func.apply(this, args), wait); }; } // 节流 function throttle(func, limit) { let inThrottle; return function(...args) { if (!inThrottle) { func.apply(this, args); inThrottle = true; setTimeout(() => inThrottle = false, limit); } }; } // 使用示例 window.addEventListener('resize', debounce(handleResize, 300)); window.addEventListener('scroll', throttle(handleScroll, 100));

5. CSS 优化

关键 CSS 内联

html
<style> /* 关键 CSS */ body { margin: 0; font-family: Arial; } .header { background: #333; color: white; } </style> <link rel="preload" href="styles/main.css" as="style" onload="this.onload=null;this.rel='stylesheet'"> <noscript><link rel="stylesheet" href="styles/main.css"></noscript>

CSS 压缩和优化

javascript
// 使用 cssnano 压缩 CSS const cssnano = require('cssnano'); const postcss = require('postcss'); postcss([cssnano]) .process(css, { from: undefined }) .then(result => { console.log(result.css); });

使用 CSS 变量

css
:root { --primary-color: #007bff; --secondary-color: #6c757d; --spacing: 1rem; } .button { background: var(--primary-color); padding: var(--spacing); }

6. 网络优化

使用 HTTP/2

nginx
# Nginx 配置 server { listen 443 ssl http2; server_name example.com; ssl_certificate /path/to/cert.pem; ssl_certificate_key /path/to/key.pem; }

启用压缩

javascript
// Express.js 中启用压缩 const compression = require('compression'); const express = require('express'); const app = express(); app.use(compression());

CDN 加速

html
<!-- 使用 CDN 加载资源 --> <link rel="stylesheet" href="https://cdn.example.com/styles/main.css"> <script src="https://cdn.example.com/scripts/app.js"></script>

7. 性能监控

使用 Performance API

javascript
// 页面加载时间 window.addEventListener('load', () => { const perfData = performance.getEntriesByType('navigation')[0]; console.log('Page load time:', perfData.loadEventEnd - perfData.fetchStart); console.log('DOM ready time:', perfData.domContentLoadedEventEnd - perfData.fetchStart); }); // 资源加载时间 const resources = performance.getEntriesByType('resource'); resources.forEach(resource => { console.log(`${resource.name}: ${resource.duration}ms`); });

使用 Web Vitals

javascript
import { getCLS, getFID, getFCP, getLCP, getTTFB } from 'web-vitals'; getCLS(console.log); getFID(console.log); getFCP(console.log); getLCP(console.log); getTTFB(console.log);

8. Service Worker 优化

优化 Service Worker 更新

javascript
// 定期检查更新 setInterval(() => { navigator.serviceWorker.getRegistration().then(registration => { if (registration) { registration.update(); } }); }, 60 * 60 * 1000); // 每小时检查一次

使用 Cache Storage API

javascript
// 检查缓存大小 async function getCacheSize() { const cache = await caches.open('my-cache'); const keys = await cache.keys(); let totalSize = 0; for (const request of keys) { const response = await cache.match(request); const blob = await response.blob(); totalSize += blob.size; } console.log(`Cache size: ${(totalSize / 1024 / 1024).toFixed(2)} MB`); }

最佳实践总结

  1. 预缓存关键资源:确保首次加载快速
  2. 使用合适的缓存策略:根据资源类型选择策略
  3. 优化图片:使用现代格式和响应式图片
  4. 代码分割:减少初始加载时间
  5. 懒加载:延迟加载非关键资源
  6. 压缩资源:减小文件体积
  7. 使用 CDN:加速资源加载
  8. 监控性能:持续跟踪和优化性能指标
  9. 定期更新缓存:确保内容新鲜度
  10. 测试不同网络条件:确保在各种网络下都有良好体验
标签:PWA