Master the art of web performance optimization to create lightning-fast web applications that users love. 🚀
Core Web Vitals 📊
Understanding and optimizing for Core Web Vitals is crucial for modern web development.
// Measure Largest Contentful Paint (LCP)
new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
console.log('LCP:', entry.startTime, entry);
}
}).observe({ type: 'largest-contentful-paint', buffered: true });
// Measure First Input Delay (FID)
new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
console.log('FID:', entry.processingStart - entry.startTime, entry);
}
}).observe({ type: 'first-input', buffered: true });
// Measure Cumulative Layout Shift (CLS)
new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
console.log('CLS:', entry.value, entry);
}
}).observe({ type: 'layout-shift', buffered: true });
Code Splitting 📦
React Code Splitting
// Before ❌
import HeavyComponent from './HeavyComponent';
// After ✅
const HeavyComponent = React.lazy(() => import('./HeavyComponent'));
function MyComponent() {
return (
<Suspense fallback={<LoadingSpinner />}>
<HeavyComponent />
</Suspense>
);
}
// Route-based code splitting
import { Routes, Route } from 'react-router-dom';
const Home = React.lazy(() => import('./routes/Home'));
const Dashboard = React.lazy(() => import('./routes/Dashboard'));
const Settings = React.lazy(() => import('./routes/Settings'));
function App() {
return (
<Routes>
<Route path="/" element={
<Suspense fallback={<LoadingSpinner />}>
<Home />
</Suspense>
} />
<Route path="/dashboard" element={
<Suspense fallback={<LoadingSpinner />}>
<Dashboard />
</Suspense>
} />
<Route path="/settings" element={
<Suspense fallback={<LoadingSpinner />}>
<Settings />
</Suspense>
} />
</Routes>
);
}
Image Optimization 🖼️
// Next.js Image Component
import Image from 'next/image';
function OptimizedImage() {
return (
<Image
src="/large-image.jpg"
alt="Optimized Image"
width={800}
height={600}
placeholder="blur"
blurDataURL="data:image/jpeg;base64,..."
loading="lazy"
quality={75}
/>
);
}
// Responsive Images with srcset
function ResponsiveImage() {
return (
<img
src="image-800w.jpg"
srcset="image-400w.jpg 400w,
image-800w.jpg 800w,
image-1200w.jpg 1200w"
sizes="(max-width: 400px) 400px,
(max-width: 800px) 800px,
1200px"
loading="lazy"
alt="Responsive Image"
/>
);
}
Caching Strategies 🗄️
// Service Worker Cache
const CACHE_NAME = 'my-site-cache-v1';
const urlsToCache = [
'/',
'/styles/main.css',
'/scripts/main.js'
];
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME)
.then((cache) => cache.addAll(urlsToCache))
);
});
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;
}
);
})
);
});
// Browser Storage Strategy
class StorageManager {
constructor() {
this.storage = localStorage;
this.memoryCache = new Map();
}
set(key, value, ttl = 3600000) { // Default TTL: 1 hour
const item = {
value,
timestamp: Date.now(),
ttl
};
this.memoryCache.set(key, item);
this.storage.setItem(key, JSON.stringify(item));
}
get(key) {
// Check memory cache first
const memoryItem = this.memoryCache.get(key);
if (memoryItem) {
if (Date.now() - memoryItem.timestamp < memoryItem.ttl) {
return memoryItem.value;
}
this.memoryCache.delete(key);
}
// Check localStorage
const item = JSON.parse(this.storage.getItem(key));
if (item && Date.now() - item.timestamp < item.ttl) {
this.memoryCache.set(key, item);
return item.value;
}
// Remove expired item
this.storage.removeItem(key);
return null;
}
}
const storage = new StorageManager();
storage.set('user', { name: 'John' }, 1800000); // 30 minutes TTL
Lazy Loading 🐌
// Intersection Observer for Lazy Loading
function lazyLoad() {
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));
}
// Dynamic Import with Intersection Observer
function lazyLoadComponent() {
const placeholders = document.querySelectorAll('[data-component]');
const componentObserver = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const element = entry.target;
const componentName = element.dataset.component;
import(`./components/${componentName}.js`)
.then(module => {
const component = new module.default();
element.replaceWith(component.render());
observer.unobserve(element);
})
.catch(error => console.error('Error loading component:', error));
}
});
});
placeholders.forEach(placeholder => componentObserver.observe(placeholder));
}
Performance Monitoring 📈
// Performance Metrics Collection
const metrics = {
collect() {
const navigation = performance.getEntriesByType('navigation')[0];
const paint = performance.getEntriesByType('paint');
return {
// Navigation Timing
dnsLookup: navigation.domainLookupEnd - navigation.domainLookupStart,
tcpConnection: navigation.connectEnd - navigation.connectStart,
serverResponse: navigation.responseEnd - navigation.requestStart,
domLoad: navigation.domContentLoadedEventEnd - navigation.domContentLoadedEventStart,
pageLoad: navigation.loadEventEnd - navigation.loadEventStart,
// Paint Timing
firstPaint: paint.find(entry => entry.name === 'first-paint')?.startTime,
firstContentfulPaint: paint.find(entry => entry.name === 'first-contentful-paint')?.startTime,
// Memory
jsHeapSize: performance.memory?.usedJSHeapSize,
// Resource Timing
resources: performance.getEntriesByType('resource').map(resource => ({
name: resource.name,
duration: resource.duration,
size: resource.transferSize,
type: resource.initiatorType
}))
};
},
send(data) {
// Send to analytics service
navigator.sendBeacon('/analytics', JSON.stringify(data));
}
};
// Usage
window.addEventListener('load', () => {
// Wait for all resources to load
setTimeout(() => {
const performanceData = metrics.collect();
metrics.send(performanceData);
}, 0);
});
Bundle Optimization 📦
// webpack.config.js
const path = require('path');
const TerserPlugin = require('terser-webpack-plugin');
const CompressionPlugin = require('compression-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[contenthash].js',
chunkFilename: '[name].[contenthash].chunk.js'
},
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
terserOptions: {
compress: {
drop_console: true,
drop_debugger: true
}
}
})
],
splitChunks: {
chunks: 'all',
minSize: 20000,
minRemainingSize: 0,
minChunks: 1,
maxAsyncRequests: 30,
maxInitialRequests: 30,
enforceSizeThreshold: 50000,
cacheGroups: {
defaultVendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10,
reuseExistingChunk: true
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
}
}
}
},
plugins: [
new CompressionPlugin({
algorithm: 'gzip',
test: /\.(js|css|html|svg)$/,
threshold: 10240,
minRatio: 0.8
})
]
};
Best Practices 📝
- Implement proper code splitting
- Use lazy loading for images and components
- Optimize bundle size
- Implement proper caching strategies
- Monitor performance metrics
- Optimize images and media
- Minimize server response time
- Reduce JavaScript execution time
- Implement proper error handling
- Follow Core Web Vitals guidelines
Additional Resources
Web performance optimization is an ongoing process that requires constant monitoring and improvement. By following these techniques and best practices, you can create fast, responsive web applications that provide an excellent user experience.