44 lines
1.3 KiB
TypeScript
44 lines
1.3 KiB
TypeScript
/**
|
||
* Web Vitals 采集(A11y + 性能)
|
||
*
|
||
* 仲裁:所有前端应用必须采集 Web Vitals
|
||
* 端点:NEXT_PUBLIC_WEB_VITALS_ENDPOINT(默认 /api/admin/web-vitals)
|
||
*/
|
||
import { onLCP, onCLS, onFCP, onINP, onTTFB } from "web-vitals";
|
||
|
||
type Metric = { name: string; value: number; rating: string; id: string };
|
||
|
||
const ENDPOINT =
|
||
process.env.NEXT_PUBLIC_WEB_VITALS_ENDPOINT ?? "/api/admin/web-vitals";
|
||
|
||
function sendMetric(metric: Metric): void {
|
||
if (typeof navigator === "undefined") return;
|
||
const body = JSON.stringify({
|
||
name: metric.name,
|
||
value: metric.value,
|
||
rating: metric.rating,
|
||
id: metric.id,
|
||
page: window.location.pathname,
|
||
ts: Date.now(),
|
||
});
|
||
// 使用 sendBeacon 避免阻塞卸载
|
||
if (navigator.sendBeacon) {
|
||
navigator.sendBeacon(ENDPOINT, body);
|
||
} else {
|
||
void fetch(ENDPOINT, { method: "POST", body, keepalive: true }).catch(
|
||
() => {
|
||
// 忽略上报失败
|
||
},
|
||
);
|
||
}
|
||
}
|
||
|
||
export function initWebVitals(): void {
|
||
if (typeof window === "undefined") return;
|
||
onLCP((m) => sendMetric(m as unknown as Metric));
|
||
onCLS((m) => sendMetric(m as unknown as Metric));
|
||
onFCP((m) => sendMetric(m as unknown as Metric));
|
||
onINP((m) => sendMetric(m as unknown as Metric));
|
||
onTTFB((m) => sendMetric(m as unknown as Metric));
|
||
}
|