Web Development
Frontend Performance
Google research: every 100ms improvement in load speed = +1% conversion. Amazon: 100ms delay = -1% revenue. Pinterest: 40% faster loading = +15% registrations. Frontend performance is not a technical metric - it is a business metric.
- **Google** uses Core Web Vitals as a ranking signal since 2021 - slow sites lose search positions
- **Shopify** reported a 35% conversion improvement after switching to statically generated pages with aggressive caching
- **Twitter Lite** with code splitting and Service Worker: 30% lower bounce rate on slow networks in emerging markets
Core Web Vitals: LCP, CLS, INP
Google measures "user experience quality" with three metrics that influence SEO ranking. **Core Web Vitals** are not abstract numbers: each one measures a specific scenario that frustrates real users.
**LCP (Largest Contentful Paint)** - when the main content is visible (image, headline). Good: under 2.5s. **CLS (Cumulative Layout Shift)** - how much content jumps during loading. Good: below 0.1. **INP (Interaction to Next Paint)** - delay between a user click and the UI updating. Good: under 200ms.
**Tools:** Chrome DevTools (Lighthouse), PageSpeed Insights, Web Vitals extension. Important: Lab data (Lighthouse) differs from Field data (real users). Google Search Console shows real CWV for the site.
An image on the page shifts down when a banner above it loads. Which Core Web Vital is affected?
Lazy Loading and Intersection Observer
A page loads 30 images, of which the user sees only the first 3. The other 27 consume bandwidth and slow loading - for nothing. **Lazy Loading** defers loading resources until they are needed: images until they appear in the viewport, components until first use.
**Intersection Observer API** is the native way to track when an element enters the viewport. The old approach used a `scroll` event plus `getBoundingClientRect()` - expensive (layout thrashing). Intersection Observer works asynchronously and does not block the main thread.
**The LCP image must not be lazy!** The hero image at the top of the page is often the LCP element. Adding `loading="lazy"` causes the browser to defer it, and LCP suffers. Rule: `loading="lazy"` only for below-the-fold images.
Why is Intersection Observer better than a scroll event for lazy loading?
Code Splitting and Dynamic Import
Webpack bundles all JS into one file. 2MB of JavaScript loads, parses, and executes even for users who only visit the homepage and never open settings. **Code Splitting** breaks the bundle into chunks that load only when needed.
**Dynamic import** is the key tool: `import()` returns a Promise and is a bundle split point. Webpack/Vite automatically extract a dynamically imported module into a separate chunk. Route-based splitting in Next.js does this automatically for each page.
**Optimal chunk size:** 50-150KB after gzip for initially loaded chunks. Too many small chunks = many HTTP requests (under HTTP/1.1). Too large = long parse time. HTTP/2 multiplexing reduces the cost of additional requests.
A user opens the homepage. A settings modal opens in 5% of cases. Code splitting for settings:
HTTP Caching and Service Workers
The best network request is the one that does not happen. **HTTP caching** lets the browser reuse previously loaded resources. Correctly configured caching is the difference between "page loads in 3 seconds" and "page is instant on repeat visits".
A **Service Worker** is a proxy server inside the browser: it intercepts network requests and can return a response from cache. Unlike HTTP cache (only GET, controlled by the server), a Service Worker can cache POST requests, dynamically choose a caching strategy, and work offline.
**Cache busting:** when new code is deployed, old files remain in browser cache. Solution: include contenthash in the filename (`main.a1b2c3.js`). Webpack/Vite do this automatically. The HTML document is not cached aggressively - it always references current hashed asset filenames.
A 100 score in Lighthouse means excellent real-world user experience.
Lighthouse is a lab measurement under ideal conditions. Real Core Web Vitals from the Chrome User Experience Report (CrUX) differ: slow devices, unstable mobile networks.
Lighthouse runs on a fast laptop with fast internet. The P75 of real users is a completely different environment. Optimize based on Field Data from Search Console.
A JS bundle is deployed as `main.js` (no hash). After deploying new code, users receive:
Frontend Performance
- CWV: LCP < 2.5s (main content), CLS < 0.1 (stability), INP < 200ms (responsiveness) - affect SEO
- Lazy loading: loading='lazy' for below-fold images; Intersection Observer for custom logic; hero image must NOT be lazy
- Code splitting: React.lazy + dynamic import moves code into a separate chunk; loads only when used
- HTTP cache: immutable assets with contenthash cached forever; HTML no-cache; Service Worker for offline and complex strategies
Related Topics
Frontend performance is closely tied to rendering and the network layer:
- SSR and SSG: Next.js — Streaming SSR directly improves LCP and TTFB
- HTTP and CDN — CDN + correct Cache-Control is the foundation of fast asset delivery
- Progressive Web Apps — Service Worker is the foundation of PWA offline capabilities
Вопросы для размышления
- How to prioritize optimizations - where to start: LCP, CLS, or INP?
- In what circumstances can aggressive caching hurt users, and how to protect against it?
- How do first-visit and repeat-visit performance differ, and should one be prioritized over the other?