101 lines
2.8 KiB
TypeScript
101 lines
2.8 KiB
TypeScript
import { onCLS, onFCP, onLCP, onTTFB, onINP, type Metric } from 'web-vitals';
|
|
|
|
function sendToAnalytics(metric: Metric) {
|
|
// Log to console in development
|
|
if (import.meta.env.DEV) {
|
|
console.log('Web Vitals:', {
|
|
name: metric.name,
|
|
value: metric.value,
|
|
rating: metric.rating,
|
|
delta: metric.delta,
|
|
id: metric.id,
|
|
});
|
|
}
|
|
|
|
// Send to Google Analytics if available
|
|
if (typeof window !== 'undefined' && (window as any).gtag) {
|
|
(window as any).gtag('event', metric.name, {
|
|
value: Math.round(metric.name === 'CLS' ? metric.value * 1000 : metric.value),
|
|
event_category: 'Web Vitals',
|
|
event_label: metric.id,
|
|
non_interaction: true,
|
|
});
|
|
}
|
|
|
|
// Send to custom analytics endpoint if needed
|
|
if (import.meta.env.PROD && import.meta.env.VITE_ANALYTICS_ENDPOINT) {
|
|
fetch(import.meta.env.VITE_ANALYTICS_ENDPOINT, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
metric: metric.name,
|
|
value: metric.value,
|
|
rating: metric.rating,
|
|
id: metric.id,
|
|
timestamp: Date.now(),
|
|
url: window.location.href,
|
|
userAgent: navigator.userAgent,
|
|
}),
|
|
keepalive: true,
|
|
}).catch((error) => {
|
|
console.error('Failed to send web vitals:', error);
|
|
});
|
|
}
|
|
}
|
|
|
|
export function reportWebVitals() {
|
|
// Core Web Vitals
|
|
onCLS(sendToAnalytics);
|
|
onLCP(sendToAnalytics);
|
|
onINP(sendToAnalytics); // Replaces deprecated FID
|
|
|
|
// Other important metrics
|
|
onFCP(sendToAnalytics);
|
|
onTTFB(sendToAnalytics);
|
|
}
|
|
|
|
// Performance observer for additional metrics
|
|
export function observePerformance() {
|
|
if (typeof window === 'undefined' || !('PerformanceObserver' in window)) {
|
|
return;
|
|
}
|
|
|
|
// Observe long tasks (blocking the main thread)
|
|
try {
|
|
const longTaskObserver = new PerformanceObserver((list) => {
|
|
for (const entry of list.getEntries()) {
|
|
if (entry.duration > 50) {
|
|
console.warn('Long Task detected:', {
|
|
duration: entry.duration,
|
|
startTime: entry.startTime,
|
|
name: entry.name,
|
|
});
|
|
}
|
|
}
|
|
});
|
|
longTaskObserver.observe({ entryTypes: ['longtask'] });
|
|
} catch (e) {
|
|
// Long task API not supported
|
|
}
|
|
|
|
// Observe layout shifts
|
|
try {
|
|
const layoutShiftObserver = new PerformanceObserver((list) => {
|
|
for (const entry of list.getEntries()) {
|
|
if ((entry as any).hadRecentInput) continue;
|
|
const value = (entry as any).value;
|
|
if (value > 0.1) {
|
|
console.warn('Large Layout Shift:', {
|
|
value,
|
|
startTime: entry.startTime,
|
|
sources: (entry as any).sources,
|
|
});
|
|
}
|
|
}
|
|
});
|
|
layoutShiftObserver.observe({ entryTypes: ['layout-shift'] });
|
|
} catch (e) {
|
|
// Layout shift API not supported
|
|
}
|
|
}
|