commit
32a36cceff
444 files changed
+67186
No files matched your search
@@ -0,0 +1,121 @@
|
||||
interface PerformanceMetrics {
|
||||
lcp: number | null;
|
||||
fid: number | null;
|
||||
cls: number | null;
|
||||
fcp: number | null;
|
||||
ttfb: number | null;
|
||||
pageLoad: number | null;
|
||||
}
|
||||
|
||||
interface ApiTiming {
|
||||
name: string;
|
||||
duration: number;
|
||||
timestamp: number;
|
||||
}
|
||||
|
||||
interface ComponentTiming {
|
||||
name: string;
|
||||
duration: number;
|
||||
timestamp: number;
|
||||
}
|
||||
|
||||
interface LayoutShift extends PerformanceEntry {
|
||||
value: number;
|
||||
hadRecentInput: boolean;
|
||||
}
|
||||
|
||||
class PerformanceService {
|
||||
private metrics: PerformanceMetrics = {
|
||||
lcp: null,
|
||||
fid: null,
|
||||
cls: 0,
|
||||
fcp: null,
|
||||
ttfb: null,
|
||||
pageLoad: null,
|
||||
};
|
||||
private apiTimings: ApiTiming[] = [];
|
||||
private componentTimings: ComponentTiming[] = [];
|
||||
|
||||
constructor() {
|
||||
if (typeof window !== 'undefined' && typeof PerformanceObserver !== 'undefined') {
|
||||
this.observeLCP();
|
||||
this.observeFID();
|
||||
this.observeCLS();
|
||||
this.observeFCP();
|
||||
}
|
||||
}
|
||||
|
||||
private observeFCP() {
|
||||
new PerformanceObserver((entryList) => {
|
||||
for (const entry of entryList.getEntries()) {
|
||||
if (entry.name === 'first-contentful-paint') {
|
||||
this.metrics.fcp = entry.startTime;
|
||||
}
|
||||
}
|
||||
}).observe({ type: 'paint', buffered: true });
|
||||
}
|
||||
|
||||
private observeLCP() {
|
||||
new PerformanceObserver((entryList) => {
|
||||
const entries = entryList.getEntries();
|
||||
if (entries.length > 0) {
|
||||
this.metrics.lcp = entries[entries.length - 1].startTime;
|
||||
}
|
||||
}).observe({ type: 'largest-contentful-paint', buffered: true });
|
||||
}
|
||||
|
||||
private observeFID() {
|
||||
new PerformanceObserver((entryList) => {
|
||||
for (const entry of entryList.getEntries()) {
|
||||
this.metrics.fid = (entry as PerformanceEventTiming).processingStart - entry.startTime;
|
||||
}
|
||||
}).observe({ type: 'first-input', buffered: true });
|
||||
}
|
||||
|
||||
private observeCLS() {
|
||||
new PerformanceObserver((entryList) => {
|
||||
for (const entry of entryList.getEntries()) {
|
||||
if (!(entry as LayoutShift).hadRecentInput) {
|
||||
this.metrics.cls += (entry as LayoutShift).value;
|
||||
}
|
||||
}
|
||||
}).observe({ type: 'layout-shift', buffered: true });
|
||||
}
|
||||
|
||||
public getMetrics(): PerformanceMetrics {
|
||||
if (typeof performance !== 'undefined') {
|
||||
const navEntry = performance.getEntriesByType('navigation')[0] as
|
||||
| PerformanceNavigationTiming
|
||||
| undefined;
|
||||
if (navEntry) {
|
||||
this.metrics.ttfb = navEntry.responseStart - navEntry.requestStart;
|
||||
this.metrics.pageLoad = navEntry.loadEventEnd - navEntry.startTime;
|
||||
}
|
||||
}
|
||||
return this.metrics;
|
||||
}
|
||||
|
||||
public recordApiTiming(name: string, duration: number) {
|
||||
this.apiTimings.unshift({ name, duration, timestamp: Date.now() });
|
||||
if (this.apiTimings.length > 10) {
|
||||
this.apiTimings.pop();
|
||||
}
|
||||
}
|
||||
|
||||
public getApiTimings(): ApiTiming[] {
|
||||
return this.apiTimings;
|
||||
}
|
||||
|
||||
public recordComponentTiming(name: string, duration: number) {
|
||||
this.componentTimings.unshift({ name, duration, timestamp: Date.now() });
|
||||
if (this.componentTimings.length > 10) {
|
||||
this.componentTimings.pop();
|
||||
}
|
||||
}
|
||||
|
||||
public getComponentTimings(): ComponentTiming[] {
|
||||
return this.componentTimings;
|
||||
}
|
||||
}
|
||||
|
||||
export const performanceService = new PerformanceService();
|
||||
Reference in new issue
Block a user