web-vitals: Google's Official Toolkit for Real User Performance Monitoring
Frontend Performance Monitoring: web-vitals
The web-vitals Library: The Officially Recommended Standardized Measurement Tool
To accurately collect Web Vitals metrics, the most reliable approach is to use the web-vitals open-source library maintained by Google. This lightweight library is built entirely on the browser-native PerformanceObserver API. Its measurement results are fully aligned with official tools such as Chrome, PageSpeed Insights, and Search Console, making it the de facto industry standard.
Core Metrics
- LCP (Largest Contentful Paint): Measures page loading speed. Ideal ≤ 2.5 seconds, 2.5–4 seconds needs improvement, > 4 seconds is poor.
- INP (Interaction to Next Paint): Measures interaction responsiveness across the full page lifecycle. Ideal ≤ 200 milliseconds, 200–500 milliseconds needs improvement, > 500 milliseconds is poor.
- CLS (Cumulative Layout Shift): Measures visual stability. Ideal ≤ 0.1, 0.1–0.25 needs improvement, > 0.25 is poor.
Design Details
- Low Intrusiveness: The library itself is under 2 KB, adding no extra performance burden to the page.
- Buffering Mechanism: By enabling the
bufferedflag on PerformanceObserver, even if the library loads late, it can capture performance events that occurred early in the page lifecycle, preventing loss of critical first-screen data. - Modular Design: Supports importing only the measurement functions needed for specific metrics, further reducing bundle size.
- Version Iteration: Starting from v3, core measurement functions were renamed from
getXXX()toonXXX(), more accurately expressing the semantics of "listening for metric changes and triggering callbacks." The official API will maintain backward compatibility with the old naming until v4.
Practical Usage
import { onLCP, onINP, onCLS } from 'web-vitals';
// Monitor LCP
onLCP((metric) => {
console.log('LCP value:', metric.value, 'ms');
// Can report data to your own performance monitoring platform
});
// Monitor INP
onINP((metric) => {
console.log('INP value:', metric.value, 'ms');
});
// Monitor CLS
onCLS((metric) => {
console.log('CLS value:', metric.value);
});
For deep-dive debugging, an extended attribution module provides root cause information for metric anomalies:
import { onLCP } from 'web-vitals/attribution';
onLCP((metric) => {
console.log('LCP culprit element:', metric.attribution.element);
console.log('Resource load delay:', metric.attribution.resourceLoadDelay);
});
This attribution capability directly identifies "which element caused high LCP" or "which resource blocked rendering," significantly reducing the cost of diagnosing performance bottlenecks.
How to Optimize Metrics
LCP Optimization
- Convert first-screen largest images to WebP/AVIF format with lossless compression.
- Preload critical first-screen resources using
<link rel="preload">with high priority. - Keep server response time under 800ms; serve static assets via CDN.
- Tree-shake first-screen JS/CSS to remove unused code.
- Configure browser caching so repeat visits don't reload core resources.
- Keep the homepage design simple and focused on key content.
- Use SSR to render HTML directly.
INP Optimization
- Split all long tasks exceeding 50ms; defer non-urgent logic to idle periods.
- Set third-party scripts (analytics, ads) to
deferor load dynamically after pageonLoad. - Clean up redundant event listeners; remove duplicate or useless callbacks.
- For long lists with over 100 items, implement virtual scrolling to keep rendered DOM nodes under 50.
CLS Optimization
- Set explicit width and height attributes on all images, videos, and iframes to reserve space.
- Pre-define fixed-size containers for dynamic ads and popups to prevent sudden insertion.
- Minimize UI elements that jump around during interaction.
- Provide placeholder elements of equal size for all lazy-loaded modules so they don't trigger layout shifts when resources load.
Business Scenarios
Editing Rich Text to Produce an Article or Notification
Poor design: Enter a Vue route page with an article ID in the URL, fetch the rich text content, and insert it into the page using v-html.
Problems and solutions:
- For a high-traffic article page, fetching content on every visit and inserting it into the DOM leads to poor LCP and prevents caching. Recommendation: Use SSR, or generate static HTML files served via OSS to leverage HTTP caching.
- Images and dynamic modules within the article need lazy loading and placeholder handling. Without this, a suddenly loaded image can push content down, creating a poor user experience and bad CLS.
Menu or Module Customization Settings
Good interaction design: Alipay's home screen service customization. Adding, deleting, or dragging to reorder apps are all purely frontend actions; the final result is submitted to the server only when the user clicks "Save."
Poor interaction design: Some designers make every add, delete, or drag operation take effect immediately. For developers, this means calling three APIs per action: save, fetch added items, and fetch unadded items.
Problem: The second approach requires three API calls per operation, inevitably leading to sluggish interactions and poor INP.
Summary
The essence of Web Vitals is not a scoring system to appease search engines, but a design philosophy centered on user perception. Often, we don't need to chase perfect numbers; instead, we should find a balance between business functionality and user experience: prioritize optimizing the core paths users interact with most, and invest resources where they truly improve the user experience.