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

How to optimize PWA performance? What are the key performance optimization strategies?

2月18日 21:53

PWA performance optimization is crucial for providing a good user experience. Here are comprehensive performance optimization strategies:

1. Resource Loading Optimization

Pre-cache Critical Resources

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()) ); });

Lazy Load Non-critical Resources

javascript
// Image lazy loading 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)); // Component lazy loading const LazyComponent = React.lazy(() => import('./LazyComponent'));

Code Splitting

javascript
// Use dynamic import for code splitting async function loadFeature() { const module = await import('./feature.js'); module.init(); } // Code splitting in React const Home = React.lazy(() => import('./Home')); const About = React.lazy(() => import('./About'));

2. Cache Strategy Optimization

Smart Cache Strategies

javascript
self.addEventListener('fetch', event => { const url = new URL(event.request.url); // Static resources: cache first if (url.pathname.match(/\.(css|js|png|jpg|jpeg|gif|svg|woff|woff2)$/)) { event.respondWith(cacheFirst(event.request)); } // API requests: network first else if (url.pathname.startsWith('/api/')) { event.respondWith(networkFirst(event.request)); } // HTML documents: network first, fallback to cache else if (event.request.mode === 'navigate') { event.respondWith(networkFirstWithFallback(event.request)); } // Others: stale while revalidate 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; }); }

Cache Version Management

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. Image Optimization

Use Modern Image Formats

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>

Responsive Images

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" >

Image Compression and Optimization

javascript
// Use sharp library to compress images const sharp = require('sharp'); async function optimizeImage(inputPath, outputPath) { await sharp(inputPath) .resize(800, 600, { fit: 'inside' }) .webp({ quality: 80 }) .toFile(outputPath); }

4. JavaScript Optimization

Reduce Bundle Size

javascript
// Use Tree Shaking // Only import needed functions import { debounce } from 'lodash-es'; // Avoid importing entire library // import _ from 'lodash'; // ❌ Avoid

Use Web Workers

javascript
// Main thread 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); };

Debounce and Throttle

javascript
// Debounce function debounce(func, wait) { let timeout; return function(...args) { clearTimeout(timeout); timeout = setTimeout(() => func.apply(this, args), wait); }; } // Throttle function throttle(func, limit) { let inThrottle; return function(...args) { if (!inThrottle) { func.apply(this, args); inThrottle = true; setTimeout(() => inThrottle = false, limit); } }; } // Usage example window.addEventListener('resize', debounce(handleResize, 300)); window.addEventListener('scroll', throttle(handleScroll, 100));

5. CSS Optimization

Inline Critical CSS

html
<style> /* Critical 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 Compression and Optimization

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

Use CSS Variables

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

6. Network Optimization

Use HTTP/2

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

Enable Compression

javascript
// Enable compression in Express.js const compression = require('compression'); const express = require('express'); const app = express(); app.use(compression());

CDN Acceleration

html
<!-- Load resources from CDN --> <link rel="stylesheet" href="https://cdn.example.com/styles/main.css"> <script src="https://cdn.example.com/scripts/app.js"></script>

7. Performance Monitoring

Use Performance API

javascript
// Page load time 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); }); // Resource load time const resources = performance.getEntriesByType('resource'); resources.forEach(resource => { console.log(`${resource.name}: ${resource.duration}ms`); });

Use 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 Optimization

Optimize Service Worker Updates

javascript
// Periodically check for updates setInterval(() => { navigator.serviceWorker.getRegistration().then(registration => { if (registration) { registration.update(); } }); }, 60 * 60 * 1000); // Check every hour

Use Cache Storage API

javascript
// Check cache size 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`); }

Best Practices Summary

  1. Pre-cache Critical Resources: Ensure fast initial load
  2. Use Appropriate Cache Strategies: Choose strategy based on resource type
  3. Optimize Images: Use modern formats and responsive images
  4. Code Splitting: Reduce initial load time
  5. Lazy Loading: Delay loading non-critical resources
  6. Compress Resources: Reduce file size
  7. Use CDN: Accelerate resource loading
  8. Monitor Performance: Continuously track and optimize performance metrics
  9. Regularly Update Cache: Ensure content freshness
  10. Test Different Network Conditions: Ensure good experience under various network conditions
标签:PWA