Optymalizacja wydajności stron internetowych

Wydajność strony internetowej ma bezpośredni wpływ na doświadczenie użytkowników, konwersje i pozycje w wyszukiwarkach. Badania pokazują, że już 100ms opóźnienia może zmniejszyć konwersje o 7%. Poznaj sprawdzone techniki optymalizacji, które znacząco przyspieszą Twoją stronę.

📊 Core Web Vitals - Kluczowe metryki

Google wprowadził Core Web Vitals jako oficjalny czynnik rankingowy. To trzy kluczowe metryki, które mierzą rzeczywiste doświadczenie użytkowników:

LCP

< 2.5s

Largest Contentful Paint - czas ładowania głównego contentu

FID

< 100ms

First Input Delay - responsywność na pierwszą interakcję

CLS

< 0.1

Cumulative Layout Shift - stabilność wizualna strony

Jak mierzyć Core Web Vitals?

🖼️ Optymalizacja obrazów

Obrazy często stanowią 60-70% rozmiaru strony. Ich optymalizacja może drastycznie poprawić wydajność.

Nowoczesne formaty obrazów

<!-- Responsive images z nowoczesnymi formatami --> <picture> <source type="image/avif" srcset="image.avif"> <source type="image/webp" srcset="image.webp"> <img src="image.jpg" alt="Description" width="800" height="600" loading="lazy"> </picture> <!-- Responsive images z różnymi rozmiarami --> <img srcset="small.webp 400w, medium.webp 800w, large.webp 1200w" sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 800px" src="medium.webp" alt="Description" loading="lazy">

Lazy Loading

/* CSS-based lazy loading */ .lazy-image { opacity: 0; transition: opacity 0.3s; } .lazy-image.loaded { opacity: 1; } /* Intersection Observer dla custom lazy loading */ const imageObserver = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { const img = entry.target; img.src = img.dataset.src; img.classList.add('loaded'); imageObserver.unobserve(img); } }); }); document.querySelectorAll('img[data-src]').forEach(img => { imageObserver.observe(img); });

💡 Kompresja obrazów

Używaj narzędzi do kompresji: ImageOptim, TinyPNG, Squoosh. AVIF oferuje o 50% lepszą kompresję niż JPEG przy podobnej jakości.

⚡ Optymalizacja zasobów

Minifikacja i kompresja

/* Gzip/Brotli compression w .htaccess */ <IfModule mod_deflate.c> AddOutputFilterByType DEFLATE text/plain AddOutputFilterByType DEFLATE text/html AddOutputFilterByType DEFLATE text/xml AddOutputFilterByType DEFLATE text/css AddOutputFilterByType DEFLATE application/xml AddOutputFilterByType DEFLATE application/xhtml+xml AddOutputFilterByType DEFLATE application/rss+xml AddOutputFilterByType DEFLATE application/javascript AddOutputFilterByType DEFLATE application/x-javascript </IfModule> /* Resource hints */ <link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="dns-prefetch" href="https://api.example.com"> <link rel="preload" href="/critical.css" as="style"> <link rel="preload" href="/hero-image.webp" as="image">

Critical CSS

<!-- Inline critical CSS --> <style> /* Critical above-the-fold styles */ body { margin: 0; font-family: system-ui; } .header { height: 60px; background: #fff; } .hero { min-height: 100vh; background: #3B82F6; } </style> <!-- Async load non-critical CSS --> <link rel="preload" href="styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'"> <noscript><link rel="stylesheet" href="styles.css"></noscript> <script> // JavaScript fallback for CSS loading if (!window.HTMLLinkElement || !HTMLLinkElement.prototype.hasOwnProperty('onload')) { var links = document.querySelectorAll('link[rel="preload"][as="style"]'); links.forEach(function(link) { link.rel = 'stylesheet'; }); } </script>

🚀 JavaScript Performance

Code Splitting i Dynamic Imports

// Dynamic imports dla lazy loading modułów async function loadComponent() { const { default: Component } = await import('./Component.js'); return Component; } // Webpack code splitting import(/* webpackChunkName: "chart" */ './chart.js') .then(module => { const chart = new module.Chart(); chart.render(); }); // Route-based code splitting w React import { lazy, Suspense } from 'react'; const Dashboard = lazy(() => import('./Dashboard')); const Profile = lazy(() => import('./Profile')); function App() { return ( <Suspense fallback={<div>Loading...</div>}> <Routes> <Route path="/dashboard" element={<Dashboard />} /> <Route path="/profile" element={<Profile />} /> </Routes> </Suspense> ); }

Optymalizacja JavaScript

// Debouncing dla input events function debounce(func, wait) { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; } const optimizedSearch = debounce((query) => { fetchSearchResults(query); }, 300); // Throttling dla scroll events function throttle(func, limit) { let inThrottle; return function() { const args = arguments; const context = this; if (!inThrottle) { func.apply(context, args); inThrottle = true; setTimeout(() => inThrottle = false, limit); } } } const optimizedScrollHandler = throttle(() => { // Scroll handling logic }, 100);

💾 Strategia cache'owania

HTTP Cache Headers

# Browser caching w .htaccess <IfModule mod_expires.c> ExpiresActive on # Images ExpiresByType image/jpg "access plus 1 month" ExpiresByType image/jpeg "access plus 1 month" ExpiresByType image/gif "access plus 1 month" ExpiresByType image/png "access plus 1 month" ExpiresByType image/webp "access plus 1 month" ExpiresByType image/avif "access plus 1 month" # CSS and JavaScript ExpiresByType text/css "access plus 1 month" ExpiresByType application/javascript "access plus 1 month" # Fonts ExpiresByType font/woff2 "access plus 1 year" # HTML ExpiresByType text/html "access plus 1 day" </IfModule> # Cache Control Headers <IfModule mod_headers.c> <filesMatch "\.(css|js|png|jpg|jpeg|gif|webp|avif|woff2)$"> Header set Cache-Control "max-age=31536000, public" </filesMatch> <filesMatch "\.(html)$"> Header set Cache-Control "max-age=86400, public, must-revalidate" </filesMatch> </IfModule>

Service Worker dla zaawansowanego cache'owania

// Service Worker registration if ('serviceWorker' in navigator) { window.addEventListener('load', () => { navigator.serviceWorker.register('/sw.js') .then(registration => { console.log('SW registered: ', registration); }) .catch(registrationError => { console.log('SW registration failed: ', registrationError); }); }); } // Service Worker - sw.js const CACHE_NAME = 'v1'; const urlsToCache = [ '/', '/css/style.css', '/js/script.js', '/images/logo.webp' ]; // Cache resources self.addEventListener('install', event => { event.waitUntil( caches.open(CACHE_NAME) .then(cache => cache.addAll(urlsToCache)) ); }); // Serve cached resources self.addEventListener('fetch', event => { event.respondWith( caches.match(event.request) .then(response => { return response || fetch(event.request); } ) ); });

📱 Mobile Performance

Touch Optimization

/* Touch-friendly buttons */ .button { min-height: 44px; min-width: 44px; padding: 12px 24px; touch-action: manipulation; } /* Reduce input latency */ input, button { touch-action: manipulation; } /* Prevent zoom on input focus iOS */ input, select, textarea { font-size: 16px; } /* Optimize scroll performance */ .scrollable { -webkit-overflow-scrolling: touch; scroll-behavior: smooth; }

Network Optimization dla mobile

// Adaptive loading based on connection if ('connection' in navigator) { const connection = navigator.connection; if (connection.effectiveType === '4g') { // Load high-quality assets loadHighQualityImages(); } else { // Load optimized assets for slower connections loadOptimizedImages(); } } // Preload critical resources only on fast connections if (navigator.connection && navigator.connection.effectiveType === '4g') { const link = document.createElement('link'); link.rel = 'preload'; link.href = '/large-hero-image.webp'; link.as = 'image'; document.head.appendChild(link); }

🔧 Narzędzia do monitorowania wydajności

Real User Monitoring (RUM)

// Performance API dla custom metrics const perfObserver = new PerformanceObserver((list) => { list.getEntries().forEach((entry) => { if (entry.entryType === 'navigation') { console.log('Page Load Time:', entry.loadEventEnd - entry.fetchStart); } if (entry.entryType === 'paint') { console.log(entry.name, entry.startTime); } }); }); perfObserver.observe({entryTypes: ['navigation', 'paint']}); // Web Vitals measurement import { getCLS, getFID, getFCP, getLCP, getTTFB } from 'web-vitals'; getCLS(console.log); getFID(console.log); getFCP(console.log); getLCP(console.log); getTTFB(console.log);

🎯 Performance Budget

📏 Zalecane budżety wydajności

🚀 Advanced Techniques

HTTP/2 Push i Server-Side Rendering

// HTTP/2 Server Push headers Link: </css/critical.css>; rel=preload; as=style Link: </js/app.js>; rel=preload; as=script Link: </fonts/main.woff2>; rel=preload; as=font; crossorigin // SSR z Next.js dla lepszego TTFB export async function getServerSideProps(context) { const data = await fetchCriticalData(); return { props: { data, }, }; }

Edge Computing i CDN

// Cloudflare Workers dla edge processing addEventListener('fetch', event => { event.respondWith(handleRequest(event.request)); }); async function handleRequest(request) { // Optimize images at the edge if (request.url.includes('/images/')) { return fetch(request.url + '?format=webp&quality=85'); } return fetch(request); }

📋 Performance Checklist

🎉 Podsumowanie

Optymalizacja wydajności to ciągły proces, który wymaga systematycznego podejścia i regularnego monitorowania. Kluczem do sukcesu jest zrozumienie, które elementy mają największy wpływ na doświadczenie użytkowników i skupienie się na nich w pierwszej kolejności.

Pamiętaj, że każda sekunda ma znaczenie - użytkownicy oczekują błyskawicznych stron, a wyszukiwarki nagradzają szybkie witryny lepszymi pozycjami. Inwestowanie w wydajność to inwestowanie w sukces Twojego biznesu online.

Chcesz znacząco przyspieszyć swoją stronę internetową? Skontaktuj się z nami - przeprowadzimy kompleksowy audyt wydajności i wdrożymy optymalizacje, które realnie wpłyną na Twój biznes.

Poprzedni artykuł Powrót do bloga