Mestre User Timing API for å skape egendefinerte, meningsfulle ytelsesmålinger. Gå utover standard web vitals for å identifisere flaskehalser og optimalisere brukeropplevelsen.
Mestring av frontend-ytelse: Et dypdykk i User Timing API
I det moderne digitale landskapet er frontend-ytelse ikke en luksus; det er et fundamentalt krav for å lykkes. For et globalt publikum kan en treg, lite responsiv nettside føre til brukerfrustrasjon, redusert engasjement og en direkte negativ innvirkning på forretningsresultatene. Vi har utmerkede standardiserte målinger som Core Web Vitals (Largest Contentful Paint, First Input Delay, Cumulative Layout Shift) som gir oss en grunnleggende forståelse av brukeropplevelsen. Men disse målingene, selv om de er avgjørende, forteller bare en del av historien.
Hva med ytelsen til applikasjonsspesifikke funksjoner? Hvor lang tid tar det før søkeresultatene vises etter at en bruker skriver inn en forespørsel? Hvor mye tid bruker den komplekse datavisualiseringskomponenten din på å rendre etter å ha mottatt data fra et API? Hvordan påvirker en ny funksjon hastigheten på ruteovergangene i din single-page application (SPA)? Standardmålinger kan ikke svare på disse granulære, forretningskritiske spørsmålene. Det er her User Timing API kommer inn, og gir utviklere muligheten til å lage egendefinerte, høypresisjons ytelsesmålinger som er skreddersydd for deres unike applikasjoner.
Denne omfattende guiden vil lede deg gjennom alt du trenger å vite for å utnytte User Timing API, fra de grunnleggende konseptene med merker og målinger til avanserte teknikker ved hjelp av PerformanceObserver. Når du er ferdig, vil du være rustet til å gå utover generiske målinger og begynne å fortelle din applikasjons unike ytelseshistorie.
Hva er Performance API? En bredere kontekst
Før vi dykker dypt inn i User Timing, er det viktig å forstå at det er en del av en større pakke med verktøy som samlet er kjent som Performance API. Dette nettleser-API-et gir tilgang til høypresisjons tidsdata relatert til navigasjon, ressursinnlasting og mer. Det globale `window.performance`-objektet er inngangsporten din til dette kraftige verktøysettet.
Performance API består av flere grensesnitt, inkludert:
- Navigation Timing: Gir detaljert tidsinformasjon om dokumentnavigasjonsprosessen, som tiden brukt på DNS-oppslag, TCP-håndtrykk og mottak av den første byten.
- Resource Timing: Tilbyr detaljerte nettverkstidsdata for hver ressurs som lastes av siden, inkludert bilder, skript og CSS-filer.
- Paint Timing: Eksponerer tidsberegninger for First Paint og First Contentful Paint.
- User Timing: Fokuset i vår artikkel, som lar utviklere lage sine egne tilpassede tidsstempler (merker) og måle varigheten mellom dem (målinger).
Disse API-ene jobber sammen for å gi en helhetlig oversikt over applikasjonens ytelse. Vårt mål i dag er å mestre User Timing-delen, som gir oss makten til å legge til våre egne tilpassede sjekkpunkter på denne ytelsestidslinjen.
Kjernekonseptene: Merker og målinger
User Timing API er villedende enkelt, og kretser rundt to grunnleggende konsepter: merker og målinger. Tenk på det som å bruke en stoppeklokke. Du trykker på en knapp for å markere en starttid, og du trykker på den igjen for å markere en sluttid. Varigheten mellom disse to trykkene er din måling.
Opprette ytelsesmerker: `performance.mark()`
Et 'merke' er et navngitt, høyoppløselig tidsstempel registrert på et spesifikt punkt i applikasjonens utførelse. Det er som å plante et flagg på ytelsestidslinjen din. Du kan lage så mange merker du trenger for å identifisere nøkkeløyeblikk i en brukerreise eller komponentlivssyklus.
Syntaksen er enkel:
performance.mark(markName, [markOptions]);
markName: En streng som representerer det unike navnet for merket ditt. Velg beskrivende navn!markOptions(valgfritt): Et objekt som kan inneholde endetail-egenskap for å legge ved ekstra metadata, og enstartTimefor å spesifisere et egendefinert tidsstempel.
Grunnleggende eksempel: Merking av en hendelse
La oss si vi vil markere begynnelsen på et viktig funksjonskall.
function processLargeDataset() {
// Plant et flagg rett før det tunge arbeidet begynner
performance.mark('processLargeDataset:start');
// ... tung beregningslogikk ...
console.log('Databehandling fullført.');
// Plant et annet flagg når det er ferdig
performance.mark('processLargeDataset:end');
}
processLargeDataset();
I dette eksempelet har vi opprettet to tidsstempler i nettleserens ytelsestidslinje: `processLargeDataset:start` og `processLargeDataset:end`. Akkurat nå er de bare tidspunkter. Deres sanne kraft frigjøres når vi bruker dem til å lage en måling.
Legge til kontekst med `detail`-egenskapen
Noen ganger er et tidsstempel alene ikke nok. Du vil kanskje inkludere ekstra kontekst om hva som skjedde i det øyeblikket. `detail`-egenskapen er perfekt for dette. Den kan inneholde alle data som kan strukturelt klones (som objekter, arrays, strenger, tall).
Tenk deg at vi markerer starten på en komponentrendring og vil vite hvor mange elementer den rendret.
function renderProductList(products) {
const itemCount = products.length;
performance.mark('ProductList:render:start', {
detail: {
itemCount: itemCount,
source: 'initial-load'
}
});
// ... komponentens rendringslogikk ...
performance.mark('ProductList:render:end');
}
const sampleProducts = new Array(1000).fill(0);
renderProductList(sampleProducts);
Denne tilleggskonteksten er uvurderlig når man analyserer ytelsesdata senere. Du kan for eksempel korrelere rendringstider med antall elementer for å se om det er et lineært eller eksponentielt forhold.
Opprette ytelsesmålinger: `performance.measure()`
En 'måling' fanger opp varigheten mellom to tidspunkter. Det er beregningen som forteller deg "hvor lenge" noe tok. Oftest vil du måle tiden mellom to av dine egendefinerte merker.
Syntaksen har noen få variasjoner:
performance.measure(measureName, startMarkOrOptions, [endMark]);
measureName: En streng som representerer det unike navnet for målingen din.startMarkOrOptions(valgfritt): En streng med navnet på startmerket. Kan også være et alternativobjekt med `start`, `end`, `duration` og `detail`.endMark(valgfritt): En streng med navnet på sluttmerket.
Grunnleggende eksempel: Måling av en funksjons varighet
La oss bygge videre på vårt `processLargeDataset`-eksempel og faktisk måle hvor lang tid det tok.
function processLargeDataset() {
performance.mark('processLargeDataset:start');
// ... tung beregningslogikk ...
performance.mark('processLargeDataset:end');
// Nå, opprett målingen
performance.measure(
'processLargeDataset:duration',
'processLargeDataset:start',
'processLargeDataset:end'
);
}
processLargeDataset();
Etter at denne koden kjører, vil nettleserens ytelsesbuffer inneholde en ny oppføring med navnet `processLargeDataset:duration`. Denne oppføringen vil ha en `duration`-egenskap som inneholder den nøyaktige tiden, i millisekunder, som gikk mellom start- og sluttmerkene.
Avanserte målingsscenarioer
`measure()`-metoden er veldig fleksibel. Du trenger ikke alltid å oppgi to merker.
- Fra navigasjonsstart til et merke: Du kan måle tiden fra sidenavigasjonen startet til et av dine egendefinerte merker. Dette er utrolig nyttig for å måle ting som "Tid til interaktiv komponent".
// Mål fra navigasjonsstart til hovedkomponenten er klar performance.measure('timeToInteractiveHeader', 'navigationStart', 'headerComponent:ready'); - Fra et merke til nå: Hvis du utelater `endMark`, vil målingen bli beregnet fra ditt `startMark` til nåværende tidspunkt.
// Mål fra startmerket til denne kodelinjen utføres performance.measure('timeSinceDataRequest', 'api:fetch:start'); - Bruke alternativobjektet: Du kan også sende et konfigurasjonsobjekt for å definere målingen, noe som er nyttig for å legge til en `detail`-egenskap.
performance.measure('complexRender:duration', { start: 'complexRender:start', end: 'complexRender:end', detail: { renderType: 'canvas' } });
Få tilgang til og tømme ytelsesoppføringer
Å lage merker og målinger er bare halve kampen. Du trenger en måte å hente ut disse dataene på for å analysere dem. `performance`-objektet gir flere metoder for dette.
performance.getEntries(): Returnerer en matrise med alle ytelsesoppføringer i bufferen (inkludert ressurstiminger, navigasjonstiminger, osv.).performance.getEntriesByType(type): Returnerer en matrise med oppføringer av en spesifikk type. Du vil oftest bruke `performance.getEntriesByType('mark')` og `performance.getEntriesByType('measure')`.performance.getEntriesByName(name, [type]): Returnerer en matrise med oppføringer med et spesifikt navn (og eventuelt en spesifikk type).
Eksempel: Logge målinger til konsollen
// Etter å ha kjørt våre tidligere eksempler...
const allMeasures = performance.getEntriesByType('measure');
console.log(allMeasures);
// Et målingsoppføringsobjekt ser omtrent slik ut:
// {
// "name": "processLargeDataset:duration",
// "entryType": "measure",
// "startTime": 12345.67,
// "duration": 150.89
// }
const specificMeasure = performance.getEntriesByName('processLargeDataset:duration');
console.log(`Behandlingen tok: ${specificMeasure[0].duration}ms`);
Viktig: Rydde opp i ytelsesbufferen
Nettleserens ytelsesbuffer er ikke uendelig. For å forhindre minnelekkasjer og holde målingene dine relevante, er det en god praksis å fjerne merkene og målingene du har opprettet når du er ferdig med dem.
performance.clearMarks([name]): Fjerner alle merker, eller bare merker med det angitte navnet.performance.clearMeasures([name]): Fjerner alle målinger, eller bare målinger med det angitte navnet.
Et vanlig mønster er å hente dataene, behandle eller sende dem, og deretter fjerne dem.
function analyzeAndClear() {
const myMeasures = performance.getEntriesByName('processLargeDataset:duration');
// Send myMeasures til en analysetjeneste...
sendToAnalytics(myMeasures);
// Rydd opp for å frigjøre minne
performance.clearMarks('processLargeDataset:start');
performance.clearMarks('processLargeDataset:end');
performance.clearMeasures('processLargeDataset:duration');
}
Praktiske, virkelige bruksområder for User Timing
Nå som vi forstår mekanikken, la oss utforske hvordan vi kan anvende User Timing API for å løse virkelige ytelsesutfordringer. Disse eksemplene er rammeverk-agnostiske og kan tilpasses enhver frontend-stakk.
1. Måling av varighet på API-kall
Å forstå hvor lenge applikasjonen din venter på data er kritisk. Du kan enkelt pakke inn logikken for datahenting med merker og målinger.
async function fetchUserData(userId) {
const markStart = `api:getUser:${userId}:start`;
const markEnd = `api:getUser:${userId}:end`;
const measureName = `api:getUser:${userId}:duration`;
performance.mark(markStart);
try {
const response = await fetch(`https://api.example.com/users/${userId}`);
if (!response.ok) {
throw new Error('Nettverksrespons var ikke ok');
}
return await response.json();
} catch (error) {
console.error('Fetch-feil:', error);
// Du kan til og med legge til detaljer om feil!
performance.mark(markEnd, { detail: { status: 'error', message: error.message } });
} finally {
// Sørg for at sluttmerket og målingen alltid opprettes
if (performance.getEntriesByName(markEnd).length === 0) {
performance.mark(markEnd, { detail: { status: 'success' } });
}
performance.measure(measureName, markStart, markEnd);
}
}
fetchUserData('123');
Dette mønsteret gir presise tidsmålinger for hvert API-kall, slik at du kan identifisere trege endepunkter direkte fra reelle brukerdata.
2. Sporing av komponent-rendringstider i SPA-er
For rammeverk som React, Vue eller Angular er måling av tiden det tar for en komponent å montere og rendre et primært bruksområde. Dette hjelper med å identifisere komplekse komponenter som kan gjøre applikasjonen din tregere.
Eksempel med React Hooks:
import React, { useLayoutEffect, useEffect, useRef } from 'react';
function MyHeavyComponent({ data }) {
const componentId = useRef(`MyHeavyComponent-${Math.random()}`).current;
const markStartName = `${componentId}:render:start`;
const markEndName = `${componentId}:render:end`;
const measureName = `${componentId}:render:duration`;
// useLayoutEffect kjører synkront etter alle DOM-mutasjoner.
// Det er det perfekte stedet å markere starten på rendringsmålingen.
useLayoutEffect(() => {
performance.mark(markStartName);
}, []); // Kjør kun ved første montering
// useEffect kjører asynkront etter at rendringen er forpliktet til skjermen.
// Dette er et godt sted å markere slutten.
useEffect(() => {
performance.mark(markEndName);
performance.measure(measureName, markStartName, markEndName);
// Logg resultatet for demonstrasjon
const measure = performance.getEntriesByName(measureName)[0];
if (measure) {
console.log(`${measureName} tok ${measure.duration}ms`);
}
// Opprydding
performance.clearMarks(markStartName);
performance.clearMarks(markEndName);
performance.clearMeasures(measureName);
}, []); // Kjør kun ved første montering
return (
// ... JSX for den tunge komponenten ...
);
}
3. Kvantifisering av kritiske brukerreiser
Den mest virkningsfulle bruken av User Timing er å måle flertrinns brukerinteraksjoner som er kritiske for virksomheten din. Dette overgår enkle tekniske målinger og måler den oppfattede hastigheten til applikasjonens kjernefunksjonalitet.
Tenk på en utsjekkingsprosess i en e-handelsbutikk:
const checkoutButton = document.getElementById('checkout-btn');
checkoutButton.addEventListener('click', () => {
// 1. Bruker klikker på 'checkout'-knappen
performance.mark('checkout:journey:start');
// ... kode for å validere handlekurv, navigere til betalingsside, osv. ...
});
// På betalingssiden, etter at betalingsskjemaet er rendret og interaktivt
function onPaymentFormReady() {
performance.mark('checkout:paymentForm:ready');
performance.measure('checkout:timeToPaymentForm', 'checkout:journey:start', 'checkout:paymentForm:ready');
}
// Etter at betalingen er vellykket behandlet og bekreftelsesskjermen vises
function onPaymentSuccess() {
performance.mark('checkout:journey:end');
performance.measure('checkout:totalJourney:duration', 'checkout:journey:start', 'checkout:journey:end');
// Nå har du to kraftige målinger å analysere og optimalisere.
}
4. A/B-testing av ytelsesforbedringer
Når du refaktorerer en kodedel eller introduserer en ny algoritme, hvordan beviser du at den faktisk er raskere for reelle brukere? User Timing gir objektive data for A/B-testing.
Tenk deg at du har to forskjellige sorteringsalgoritmer du vil teste:
function sortProducts(products, algorithmVersion) {
const markStart = `sort:v${algorithmVersion}:start`;
const markEnd = `sort:v${algorithmVersion}:end`;
const measureName = `sort:v${algorithmVersion}:duration`;
performance.mark(markStart);
if (algorithmVersion === 'A') {
// ... kjør gammel sorteringsalgoritme ...
} else {
// ... kjør ny, optimalisert sorteringsalgoritme ...
}
performance.mark(markEnd);
performance.measure(measureName, markStart, markEnd);
}
// Basert på et A/B-testingsflagg, ville du kalt den ene eller den andre.
// Senere, i analysene dine, kan du sammenligne gjennomsnittlig varighet av
// 'sort:vA:duration' vs 'sort:vB:duration' for å se hvilken som var raskest.
Visualisering og analyse av dine egendefinerte målinger
Å lage egendefinerte målinger er nytteløst hvis du ikke analyserer dataene. Det er to primære måter å tilnærme seg dette på: lokalt under utvikling og aggregert i produksjon.
Bruke nettleserens utviklerverktøy
Moderne nettlesere som Chrome og Firefox har utmerket støtte for å visualisere User Timing-merker og -målinger i sine ytelsesprofileringsverktøy.
- Åpne nettleserens utviklerverktøy (F12 eller Ctrl+Shift+I).
- Gå til Performance-fanen.
- Start opptak av en profil og utfør deretter handlingene i appen din som utløser dine egendefinerte merker og målinger.
- Stopp opptaket.
I tidslinjevisningen finner du en dedikert rad kalt Timings. Dine egendefinerte merker vil vises som vertikale linjer, og målingene dine vil vises som fargede stolper som viser varigheten. Ved å holde musepekeren over dem vil du se navnene og nøyaktige tider. Dette er en utrolig kraftig måte å feilsøke ytelsesproblemer under utvikling på.
Sende data til analyse- og RUM-tjenester
For produksjonsovervåking må du samle inn disse dataene fra brukerne dine og sende dem til et sentralt sted for aggregering og analyse. Dette er en kjernekomponent i sanntids brukermonitorering (RUM).
Den generelle arbeidsflyten er:
- Samle inn ytelsesmålingene du er interessert i.
- Formater dem til en passende payload (f.eks. JSON).
- Send payloaden til et analyse-endepunkt. Dette kan være en tredjepartstjeneste som Datadog, New Relic, Sentry, eller til og med Google Analytics (via egendefinerte hendelser), eller en egendefinert backend du kontrollerer.
function sendPerformanceData() {
// Vi bryr oss bare om våre egendefinerte applikasjonsmålinger
const appMeasures = performance.getEntriesByType('measure').filter(
(entry) => entry.name.startsWith('app:') // Bruk en navnekonvensjon!
);
if (appMeasures.length > 0) {
const payload = JSON.stringify(appMeasures.map(measure => ({
name: measure.name,
duration: measure.duration,
startTime: measure.startTime,
details: measure.detail, // Send vår rike kontekst
path: window.location.pathname // Legg til mer kontekst
})));
// Bruk navigator.sendBeacon for pålitelig, ikke-blokkerende datasending
navigator.sendBeacon('https://analytics.example.com/performance', payload);
// Rydd opp i målingene som er sendt
appMeasures.forEach(measure => {
performance.clearMeasures(measure.name);
// Fjern også de tilhørende merkene
});
}
}
// Kall denne funksjonen på et passende tidspunkt, f.eks. når siden er i ferd med å lukkes
window.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') {
sendPerformanceData();
}
});
Avanserte teknikker og beste praksis
For å virkelig mestre User Timing API, la oss se på noen avanserte funksjoner og beste praksis som vil gjøre instrumenteringen din mer robust og effektiv.
Bruke `PerformanceObserver` for asynkron overvåking
Metodene `getEntries*()` krever at du manuelt poller ytelsesbufferen. Dette har to ulemper: du kan kjøre sjekken din for sent og gå glipp av oppføringer hvis bufferen har blitt full og tømt, og polling i seg selv kan ha en liten ytelseskostnad. Den moderne, foretrukne løsningen er `PerformanceObserver`.
En `PerformanceObserver` lar deg abonnere på ytelsesoppføringshendelser. Tilbakekallingsfunksjonen din vil bli påkalt asynkront når nye oppføringer av de typene du observerer, blir registrert.
// 1. Lag en tilbakekallingsfunksjon for å håndtere nye oppføringer
const observerCallback = (list) => {
for (const entry of list.getEntries()) {
console.log('Ny måling observert:', entry.name, entry.duration);
// Her kan du umiddelbart sende oppføringen til din analysetjeneste
// uten å måtte polle eller vente.
}
};
// 2. Opprett observatørinstansen
const observer = new PerformanceObserver(observerCallback);
// 3. Start observering for oppføringstypene 'mark' og 'measure'
// Alternativet 'buffered: true' sikrer at du får med deg oppføringer som ble opprettet
// *før* observatøren ble registrert.
observer.observe({ entryTypes: ['mark', 'measure'], buffered: true });
// Nå, hver gang performance.mark() eller performance.measure() kalles hvor som helst
// i applikasjonen din, vil observerCallback bli utløst med den nye oppføringen.
// For å stoppe observeringen senere:
// observer.disconnect();
Å bruke `PerformanceObserver` er mer effektivt, mer pålitelig, og bør være ditt standardvalg for innsamling av ytelsesdata i et produksjonsmiljø.
Etabler en klar navnekonvensjon
Etter hvert som applikasjonen din vokser, vil du samle opp mange egendefinerte målinger. Uten en konsekvent navnekonvensjon vil dataene dine bli vanskelige å filtrere og analysere. Ta i bruk et mønster som gir kontekst.
En god konvensjon kan være: [appNavn]:[funksjonEllerKomponent]:[hendelsesNavn]:[status]
ecom:ProductGallery:render:startecom:ProductGallery:render:endecom:ProductGallery:render:durationadmin:DataTable:fetchApi:startadmin:DataTable:fetchApi:duration
Denne strukturen gjør det trivielt å filtrere etter alle målinger relatert til `ProductGallery` eller å finne alle `fetchApi`-varigheter på tvers av hele applikasjonen.
Abstraher til en hjelpetjeneste
For å sikre konsistens og redusere standardkode, pakk `performance`-kallene inn i din egen hjelpemodul eller tjeneste. Dette gjør det også enkelt å aktivere eller deaktivere ytelsesovervåking basert på miljøet.
// performance-service.js
const IS_PERFORMANCE_MONITORING_ENABLED = process.env.NODE_ENV === 'production' || window.location.search.includes('perf=true');
export const perfMark = (name, options) => {
if (!IS_PERFORMANCE_MONITORING_ENABLED) return;
performance.mark(name, options);
};
export const perfMeasure = (name, start, end) => {
if (!IS_PERFORMANCE_MONITORING_ENABLED) return;
performance.measure(name, start, end);
};
export const startJourney = (name) => {
perfMark(`${name}:start`);
};
export const endJourney = (name) => {
const startMark = `${name}:start`;
const endMark = `${name}:end`;
const measureName = `${name}:duration`;
perfMark(endMark);
perfMeasure(measureName, startMark, endMark);
// Fjern eventuelt merkene her
};
// I komponenten din:
// import { startJourney, endJourney } from './performance-service';
// startJourney('ecom:checkout');
// ...senere...
// endJourney('ecom:checkout');
Konklusjon: Ta kontroll over din applikasjons ytelseshistorie
Selv om standardmålinger som Core Web Vitals gir en essensiell helsesjekk for nettstedet ditt, belyser de ikke ytelsen til funksjonene og interaksjonene som gjør applikasjonen din unik. User Timing API er broen som lukker dette gapet. Det gir en enkel, men dyptgripende kraftig mekanisme for å måle det som virkelig betyr noe for brukerne dine og virksomheten din.
Ved å implementere egendefinerte merker og målinger, forvandler du ytelsesoptimalisering fra et gjettespill til en datadrevet vitenskap. Du kan identifisere nøyaktig hvilke funksjoner, komponenter eller brukerflyter som forårsaker flaskehalser, validere effekten av refaktoreringstiltakene dine med objektive tall, og til slutt bygge en raskere, mer behagelig opplevelse for ditt globale publikum.
Start i det små. Identifiser den aller viktigste brukerreisen i applikasjonen din – enten det er å søke etter et produkt, sende inn et skjema eller laste et data-dashboard. Instrumenter den med `performance.mark()` og `performance.measure()`. Analyser resultatene i utviklerverktøyene dine. Når du ser klarheten det gir, vil du være i stand til å fortelle din applikasjons komplette ytelseshistorie, en egendefinert måling om gangen.