import type { Callback } from "./types"; // import { getFirstScreenPaint } from "./firstScreenPaint"; import { onLCP, onFID, onCLS, onFCP, onTTFB } from "web-vitals"; /** * First Contentful Paint(首次内容绘制) * 浏览器首次渲染来自 DOM 的任何文本、图像(包括背景图像)、非白色 元素或 SVG 等内容的时间点 * @param callback 回调函数 */ export function getFCP(callback: Callback) { const entryHandler = (list: any) => { for (const entry of list.getEntries()) { if (entry.name === "first-contentful-paint") { observer.disconnect(); callback({ name: "FCP", value: entry.startTime, rating: entry.startTime > 2500 ? "poor" : "good", }); } } }; const observer = new PerformanceObserver(entryHandler); observer.observe({ type: "paint", buffered: true }); } /** * First Input Delay(首次输入延迟) * 用户首次尝试与页面进行交互到浏览器能够响应这段时间的长短 * @param callback 回调函数 */ export function getFID(callback: Callback): void { const entryHandler = (entryList: any) => { for (const entry of entryList.getEntries()) { observer.disconnect(); const value = entry.processingStart - entry.startTime; callback({ name: "FID", value, rating: value > 100 ? "poor" : "good", }); } }; const observer = new PerformanceObserver(entryHandler); observer.observe({ type: "first-input", buffered: true }); } /** * Largest Contentful Paint(最大内容绘制) * 页面视图中最大的可见内容元素在屏幕上完成渲染的时间点 * 这个元素通常是用户在首次访问页面时看到的主要内容部分,如一个大图片、视频的预览图或主要的文本块 * @param callback 回调函数 */ export function getLCP(callback: Callback): void { const entryHandler = (list: any) => { for (const entry of list.getEntries()) { observer.disconnect(); callback({ name: "LCP", value: entry.startTime, rating: entry.startTime > 2500 ? "poor" : "good", }); } }; const observer = new PerformanceObserver(entryHandler); observer.observe({ type: "largest-contentful-paint", buffered: true }); } /** * Cumulative Layout Shift(累积布局偏移) * 页面从开始加载到用户完成交互期间所有未预期的内容移动的总和 * @param callback 回调函数 */ export function getCLS(callback: Callback): void { let clsValue = 0; // let clsEntries = []; let sessionValue = 0; let sessionEntries: any[] = []; const entryHandler = (entryList: any) => { for (const entry of entryList.getEntries()) { // 只将不带有最近用户输入标志的布局偏移计算在内。 if (!entry.hadRecentInput) { const firstSessionEntry = sessionEntries[0]; const lastSessionEntry = sessionEntries[sessionEntries.length - 1]; // 如果条目与上一条目的相隔时间小于 1 秒且 // 与会话中第一个条目的相隔时间小于 5 秒,那么将条目 // 包含在当前会话中。否则,开始一个新会话。 if ( sessionValue && entry.startTime - lastSessionEntry.startTime < 1000 && entry.startTime - firstSessionEntry.startTime < 5000 ) { sessionValue += entry.value; sessionEntries.push(entry); } else { sessionValue = entry.value; sessionEntries = [entry]; } // 如果当前会话值大于当前 CLS 值, // 那么更新 CLS 及其相关条目。 if (sessionValue > clsValue) { clsValue = sessionValue; // clsEntries = sessionEntries; observer.disconnect(); callback({ name: "CLS", value: clsValue, rating: clsValue > 2500 ? "poor" : "good", }); } } } }; const observer = new PerformanceObserver(entryHandler); observer.observe({ type: "layout-shift", buffered: true }); } /** * Time to First Byte(首字节时间) * 从用户发起请求到浏览器接收到服务器响应的第一个字节所需的时间 * @param callback 回调函数 */ export function getTTFB(callback: Callback): void { window.addEventListener("load", () => { const { responseStart, navigationStart } = window.performance.timing || {}; const value = responseStart - navigationStart; callback({ name: "TTFB", value, rating: value > 100 ? "poor" : "good", }); }); } export function isSafari(): boolean { return ( /Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent) ); } export function getWebVitals(callback: Callback): void { // web-vitals 不兼容safari浏览器 if (isSafari()) { getFID((res) => { callback(res); }); getFCP((res) => { callback(res); }); getLCP((res) => { callback(res); }); getCLS((res) => { callback(res); }); getTTFB((res) => { callback(res); }); } else { onLCP((res) => { callback(res); }); onFID?.((res) => { callback(res); }); onCLS((res) => { callback(res); }); onFCP((res) => { callback(res); }); onTTFB((res) => { callback(res); }); } // 首屏加载时间 // 不监听首屏绘制时间 // getFirstScreenPaint((res) => { // const data = { // name: "FSP", // value: res, // rating: res > 2500 ? "poor" : "good", // }; // callback(data); // }); } export const performanceText: any = { FCP: "首次内容绘制", FID: "首次输入延迟", LCP: "最大内容绘制", CLS: "累积布局偏移", TTFB: "首字节时间", };