Mestr Resource Timing API for at diagnosticere og optimere frontend-ydeevne. Lær at måle indlæsningstiden for hver ressource, fra DNS-opslag til indholdsdownload.
Frigørelse af Frontend-Ydeevne: En Dybdegående Gennemgang af Resource Timing API
I webudviklingens verden er hastighed ikke bare en funktion; det er et grundlæggende krav for en positiv brugeroplevelse. En langsomt indlæsende hjemmeside kan føre til højere afvisningsprocenter, lavere brugerengagement og i sidste ende en negativ indvirkning på forretningsmål. Selvom værktøjer som Lighthouse og WebPageTest giver uvurderlig diagnostik på højt niveau, repræsenterer de ofte en enkelt, syntetisk test. For virkelig at forstå og optimere ydeevnen for et globalt publikum er vi nødt til at måle oplevelsen for rigtige brugere, på deres enheder, på deres netværk. Det er her, Real User Monitoring (RUM) kommer ind i billedet, og et af dets mest kraftfulde værktøjer er Resource Timing API.
Denne omfattende guide vil tage dig med på en dybdegående rejse ind i Resource Timing API. Vi vil udforske, hvad det er, hvordan man bruger det, og hvordan man omdanner dets granulære data til handlingsorienterede indsigter, der dramatisk kan forbedre din applikations indlæsningsydeevne. Uanset om du er en erfaren frontend-ingeniør eller lige er startet på din rejse mod ydeevneoptimering, vil denne artikel udstyre dig med viden til at dissekere og forstå netværksydeevnen for hvert enkelt aktiv på din side.
Hvad er Resource Timing API?
Resource Timing API er et browserbaseret JavaScript API, der giver detaljerede netværks-timingdata for hver ressource, en webside downloader. Tænk på det som en mikroskopisk linse for din sides netværksaktivitet. For hvert billede, script, stylesheet, skrifttype og API-kald (via `fetch` eller `XMLHttpRequest`) fanger dette API et højopløseligt tidsstempel for hvert trin i netværksanmodningen.
Det er en del af en større suite af Performance API'er, som arbejder sammen for at give et holistisk billede af din applikations ydeevne. Mens Navigation Timing API fokuserer på hoveddokumentets livscyklus, zoomer Resource Timing API ind på alle de afhængige ressourcer, som hoveddokumentet anmoder om.
Hvorfor er det så vigtigt?
- Granularitet: Det går ud over en enkelt "sideindlæsningstid"-metrik. Du kan præcist se, hvor lang tid DNS-opslaget, TCP-forbindelsen og indholdsdownloadet tog for et specifikt tredjepartsscript eller et kritisk heltebillede.
- Rigtige Brugerdata: I modsætning til laboratoriebaserede værktøjer kører dette API i dine brugeres browsere. Dette giver dig mulighed for at indsamle ydeevnedata fra et bredt udvalg af netværksforhold, enheder og geografiske placeringer, hvilket giver dig et sandt billede af din globale brugeroplevelse.
- Handlingsorienterede Indsigter: Ved at analysere disse data kan du lokalisere specifikke flaskehalse. Er et tredjeparts analyse-script langsomt til at oprette forbindelse? Underpræsterer din CDN i en bestemt region? Er dine billeder for store? Resource Timing API leverer de beviser, der er nødvendige for at besvare disse spørgsmål med sikkerhed.
Anatomien af en Ressourceindlæsning: Dekonstruktion af Tidslinjen
Kernen i Resource Timing API er `PerformanceResourceTiming`-objektet. For hver ressource, der indlæses, opretter browseren et af disse objekter, som indeholder et væld af timing- og størrelsesinformation. For at forstå disse objekter er det nyttigt at visualisere indlæsningsprocessen som et vandfaldsdiagram, hvor hvert trin følger det foregående.
Lad os nedbryde de vigtigste egenskaber ved et `PerformanceResourceTiming`-objekt. Alle tidsværdier er højopløselige tidsstempler målt i millisekunder fra starten af sidenavigationen (`performance.timeOrigin`).
startTime -> fetchStart -> domainLookupStart -> domainLookupEnd -> connectStart -> connectEnd -> requestStart -> responseStart -> responseEnd
Vigtige Timing-egenskaber
name: URL'en til ressourcen. Dette er din primære identifikator.entryType: En streng, der angiver typen af ydeevne-entry. Til vores formål vil dette altid være "resource".initiatorType: Dette er utroligt nyttigt til fejlfinding. Det fortæller dig, hvordan ressourcen blev anmodet. Almindelige værdier inkluderer 'img', 'link' (for CSS), 'script', 'css' (for ressourcer indlæst fra CSS som `@import`), 'fetch' og 'xmlhttprequest'.duration: Den samlede tid, ressourcen tog, beregnet somresponseEnd - startTime. Dette er den overordnede metrik for en enkelt ressource.startTime: Tidsstemplet umiddelbart før ressourcehentningen begynder.fetchStart: Tidsstemplet lige før browseren begynder at hente ressourcen. Den kan tjekke caches (HTTP-cache, Service Worker-cache), før den fortsætter til netværket. Hvis ressourcen serveres fra en cache, vil mange af de efterfølgende timing-værdier være nul.domainLookupStart&domainLookupEnd: Disse markerer starten og slutningen af DNS-opslaget (Domain Name System). Varigheden (domainLookupEnd - domainLookupStart) er den tid, det tog at oversætte domænenavnet til en IP-adresse. En høj værdi her kan indikere en langsom DNS-udbyder.connectStart&connectEnd: Disse markerer starten og slutningen af etableringen af en forbindelse til serveren. For HTTP er dette TCP trevejs-håndtrykket. Varigheden (connectEnd - connectStart) er din TCP-forbindelsestid.secureConnectionStart: Hvis ressourcen indlæses over HTTPS, markerer dette tidsstempel begyndelsen på SSL/TLS-håndtrykket. Varigheden (connectEnd - secureConnectionStart) fortæller dig, hvor lang tid krypteringsforhandlingen tog. Langsomme TLS-håndtryk kan være et tegn på server-fejlkonfiguration eller netværkslatens.requestStart: Tidsstemplet lige før browseren sender den faktiske HTTP-anmodning for ressourcen til serveren. Tiden mellemconnectEndogrequestStartkaldes ofte "request queuing"-tid, hvor browseren venter på en ledig forbindelse.responseStart: Tidsstemplet, da browseren modtager den allerførste byte af svaret fra serveren. Varigheden (responseStart - requestStart) er den berømte Time to First Byte (TTFB). En høj TTFB er næsten altid en indikator for en langsom backend-proces eller server-side latens.responseEnd: Tidsstemplet, da den sidste byte af ressourcen er modtaget, og anmodningen er lukket med succes. Varigheden (responseEnd - responseStart) repræsenterer indholdsdownloadtiden.
Egenskaber for Ressourcestørrelse
At forstå ressourcestørrelse er lige så vigtigt som at forstå timing. API'et giver tre vigtige metrikker:
transferSize: Størrelsen i bytes af ressourcen overført over netværket, inklusive headers og den komprimerede svarkrop. Hvis ressourcen blev serveret fra en cache, vil dette ofte være 0. Dette er tallet, der direkte påvirker brugerens dataabonnement og netværkstid.encodedBodySize: Størrelsen i bytes af payload-kroppen *efter* komprimering (f.eks. Gzip eller Brotli), men *før* dekomprimering. Dette hjælper dig med at forstå størrelsen af selve payloaden, adskilt fra headers.decodedBodySize: Størrelsen i bytes af payload-kroppen i dens ukomprimerede, originale form. At sammenligne dette medencodedBodySizeafslører effektiviteten af din komprimeringsstrategi. Hvis disse to tal er meget tætte for et tekstbaseret aktiv (som JS, CSS eller HTML), fungerer din komprimering sandsynligvis ikke korrekt.
Server Timing
En af de mest kraftfulde integrationer med Resource Timing API er `serverTiming`-egenskaben. Din backend kan sende ydeevnemetrikker i en speciel HTTP-header (`Server-Timing`), og disse metrikker vil fremgå af `serverTiming`-arrayet på det tilsvarende `PerformanceResourceTiming`-objekt. Dette bygger bro mellem frontend- og backend-ydeevneovervågning, så du kan se databaseforespørgselstider eller API-behandlingsforsinkelser direkte i dine frontend-data.
For eksempel kunne en backend sende denne header:
Server-Timing: db;dur=53, api;dur=47.2, cache;desc="HIT"
Disse data ville være tilgængelige i `serverTiming`-egenskaben, hvilket giver dig mulighed for at korrelere en høj TTFB med en specifik langsom proces på backend.
Sådan Får Du Adgang til Resource Timing Data i JavaScript
Nu hvor vi forstår de tilgængelige data, lad os se på de praktiske måder at indsamle dem på ved hjælp af JavaScript. Der er to primære metoder.
Metode 1: `performance.getEntriesByType('resource')`
Dette er den enkleste måde at komme i gang på. Denne metode returnerer et array af alle `PerformanceResourceTiming`-objekter for ressourcer, der allerede er færdige med at indlæse på siden på det tidspunkt, kaldet foretages.
// Vent på, at siden indlæses, for at sikre, at de fleste ressourcer fanges
window.addEventListener('load', () => {
const resources = performance.getEntriesByType('resource');
resources.forEach((resource) => {
console.log(`Ressource indlæst: ${resource.name}`);
console.log(` - Samlet tid: ${resource.duration.toFixed(2)}ms`);
console.log(` - Initiator: ${resource.initiatorType}`);
console.log(` - Overførselsstørrelse: ${resource.transferSize} bytes`);
});
});
Begrænsning: Denne metode er et øjebliksbillede. Hvis du kalder den for tidligt, går du glip af ressourcer, der endnu ikke er indlæst. Hvis din applikation dynamisk indlæser ressourcer længe efter den indledende sideindlæsning, ville du være nødt til at polle denne metode gentagne gange, hvilket er ineffektivt.
Metode 2: `PerformanceObserver` (Den Anbefalede Tilgang)
En `PerformanceObserver` er en mere moderne, robust og effektiv måde at indsamle ydeevne-entries på. I stedet for at du poller efter data, skubber browseren nye entries til din observer-callback, efterhånden som de bliver tilgængelige.
Her er hvorfor det er bedre:
- Asynkron: Den blokerer ikke hovedtråden.
- Omfattende: Den kan fange entries fra starten af sideindlæsningen og undgå race conditions, hvor et script kører, efter en ressource allerede er indlæst.
- Effektiv: Den undgår behovet for polling med `setTimeout` eller `setInterval`.
Her er en standardimplementering:
try {
const observer = new PerformanceObserver((list) => {
list.getEntries().forEach((entry) => {
// Behandl hver ressource-entry, som den kommer ind
if (entry.entryType === 'resource') {
console.log(`Ressource observeret: ${entry.name}`);
console.log(` - Time to First Byte (TTFB): ${(entry.responseStart - entry.requestStart).toFixed(2)}ms`);
}
});
});
// Begynd at observere for 'resource'-entries.
// 'buffered'-flaget sikrer, at vi får entries, der blev indlæst, før vores observer blev oprettet.
observer.observe({ type: 'resource', buffered: true });
// Du kan stoppe med at observere senere, hvis det er nødvendigt
// observer.disconnect();
} catch (e) {
console.error('PerformanceObserver understøttes ikke i denne browser.');
}
buffered: true-optionen er kritisk. Den fortæller observeren, at den straks skal sende alle `resource`-entries, der allerede er i browserens ydeevne-entry-buffer, hvilket sikrer, at du får en komplet liste fra starten.
Håndtering af Ydeevne-bufferen
Browsere har en standardgrænse for, hvor mange resource timing-entries de gemmer (typisk 150). På meget komplekse sider kan denne buffer blive fyldt. Når det sker, affyrer browseren en `resourcetimingbufferfull`-hændelse, og ingen nye entries tilføjes.
Du kan håndtere dette ved at:
- Forøge bufferstørrelsen: Brug `performance.setResourceTimingBufferSize(limit)` til at sætte en højere grænse, for eksempel 300.
- Rydde bufferen: Brug `performance.clearResourceTimings()`, efter du har behandlet entries, for at gøre plads til nye.
performance.addEventListener('resourcetimingbufferfull', () => {
console.warn('Resource Timing-buffer er fuld. Rydder...');
// Behandl først eksisterende entries fra din observer
// Ryd derefter bufferen
performance.clearResourceTimings();
// Du kan være nødt til at justere bufferstørrelsen igen, hvis dette sker hyppigt
// performance.setResourceTimingBufferSize(500);
});
Praktiske Anvendelsestilfælde og Handlingsorienterede Indsigter
Indsamling af data er kun det første skridt. Den reelle værdi ligger i at omdanne disse data til handlingsorienterede forbedringer. Lad os udforske nogle almindelige ydeevneproblemer, og hvordan Resource Timing API hjælper dig med at løse dem.
Anvendelsestilfælde 1: Identifikation af Langsomme Tredjeparts-scripts
Problemet: Tredjeparts-scripts til analyse, reklamer, kundesupport-widgets og A/B-testning er berygtede for at dræbe ydeevnen. De kan være langsomme at indlæse, blokere rendering og endda forårsage ustabilitet.
Løsningen: Brug Resource Timing API til at isolere og måle virkningen af disse scripts på dine rigtige brugere.
const observer = new PerformanceObserver((list) => {
const thirdPartyScripts = list.getEntries().filter(entry =>
entry.initiatorType === 'script' &&
!entry.name.startsWith(window.location.origin)
);
thirdPartyScripts.forEach(script => {
if (script.duration > 200) { // Sæt en tærskel, f.eks. 200ms
console.warn(`Langsomt tredjeparts-script opdaget: ${script.name}`, {
duration: `${script.duration.toFixed(2)}ms`,
transferSize: `${script.transferSize} bytes`
});
// I et rigtigt RUM-værktøj ville du sende disse data til din analytics-backend.
}
});
});
observer.observe({ type: 'resource', buffered: true });
Handlingsorienterede Indsigter:
- Høj Varighed: Hvis et script konsekvent har en lang varighed, overvej om det virkelig er nødvendigt. Kan dets funktionalitet erstattes med et mere performant alternativ?
- Indlæsningsstrategi: Bliver scriptet indlæst synkront? Brug `async`- eller `defer`-attributterne på `<script>`-tagget for at forhindre det i at blokere sidens rendering.
- Selektiv Hosting: Kan scriptet indlæses betinget, kun på sider hvor det er absolut nødvendigt?
Anvendelsestilfælde 2: Optimering af Billedlevering
Problemet: Store, uoptimerede billeder er en af de mest almindelige årsager til langsomme sideindlæsninger, især på mobile enheder med begrænset båndbredde.
Løsningen: Filtrer ressource-entries efter `initiatorType: 'img'` og analyser deres størrelse og indlæsningstider.
// ... inde i en PerformanceObserver-callback ...
list.getEntries()
.filter(entry => entry.initiatorType === 'img')
.forEach(image => {
const downloadTime = image.responseEnd - image.responseStart;
// Et stort billede kan have en høj downloadtid og en stor transferSize
if (downloadTime > 500 || image.transferSize > 100000) { // 500ms eller 100KB
console.log(`Potentielt problem med stort billede: ${image.name}`, {
downloadTime: `${downloadTime.toFixed(2)}ms`,
transferSize: `${(image.transferSize / 1024).toFixed(2)} KB`
});
}
});
Handlingsorienterede Indsigter:
- Høj `transferSize` og `downloadTime`: Dette er et klart signal om, at billedet er for stort. Optimer det ved at bruge moderne formater som WebP eller AVIF, komprimere det passende og ændre størrelsen til dets viste dimensioner.
- Brug `srcset`: Implementer responsive billeder ved hjælp af `srcset`-attributten for at servere forskellige billedstørrelser baseret på brugerens viewport.
- Lazy Loading: For billeder, der er under folden, brug `loading="lazy"` for at udsætte deres indlæsning, indtil brugeren scroller dem ind i synsfeltet.
Anvendelsestilfælde 3: Diagnosticering af Netværksflaskehalse
Problemet: Nogle gange er problemet ikke selve ressourcen, men netværksstien til den. Langsom DNS, latente forbindelser eller overbelastede servere kan alle forringe ydeevnen.
Løsningen: Nedbryd `duration` i dens komponentfaser for at lokalisere kilden til forsinkelsen.
function analyzeNetworkPhases(resource) {
const dnsTime = resource.domainLookupEnd - resource.domainLookupStart;
const tcpTime = resource.connectEnd - resource.connectStart;
const ttfb = resource.responseStart - resource.requestStart;
const downloadTime = resource.responseEnd - resource.responseStart;
console.log(`Analyse for ${resource.name}`);
if (dnsTime > 50) console.warn(` - Høj DNS-tid: ${dnsTime.toFixed(2)}ms`);
if (tcpTime > 100) console.warn(` - Høj TCP-forbindelsestid: ${tcpTime.toFixed(2)}ms`);
if (ttfb > 300) console.warn(` - Høj TTFB (langsom server): ${ttfb.toFixed(2)}ms`);
if (downloadTime > 500) console.warn(` - Langsom indholdsdownload: ${downloadTime.toFixed(2)}ms`);
}
// ... kald analyzeNetworkPhases(entry) inde i din observer ...
Handlingsorienterede Indsigter:
- Høj DNS-tid: Din DNS-udbyder kan være langsom. Overvej at skifte til en hurtigere global udbyder. Du kan også bruge `` til at løse DNS for kritiske tredjepartsdomæner på forhånd.
- Høj TCP-tid: Dette indikerer latens i etableringen af forbindelsen. Et Content Delivery Network (CDN) kan reducere dette ved at servere aktiver fra en placering, der er geografisk tættere på brugeren. Brug af `` kan udføre både DNS-opslag og TCP-håndtryk tidligt.
- Høj TTFB: Dette peger på en langsom backend. Arbejd med dit backend-team for at optimere databaseforespørgsler, forbedre server-side caching eller opgradere serverhardware. `Server-Timing`-headeren er din bedste ven her.
- Høj Download-tid: Dette er en funktion af ressourcestørrelse og netværksbåndbredde. Optimer aktivet (komprimer, minimer) eller brug en CDN for at forbedre gennemstrømningen.
Begrænsninger og Overvejelser
Selvom det er utroligt kraftfuldt, har Resource Timing API nogle vigtige begrænsninger, man skal være opmærksom på.
Ressourcer på Tværs af Oprindelse og `Timing-Allow-Origin`-headeren
Af sikkerhedsmæssige årsager begrænser browsere de timing-detaljer, der er tilgængelige for ressourcer indlæst fra en anden oprindelse (domæne, protokol eller port) end din hovedside. Som standard vil de fleste timing-egenskaber som `redirectStart`, `domainLookupStart`, `connectStart`, `requestStart`, `responseStart` og størrelsesegenskaber som `transferSize` være nul for en ressource på tværs af oprindelse.
For at eksponere disse detaljer skal serveren, der hoster ressourcen, inkludere `Timing-Allow-Origin` (TAO) HTTP-headeren. For eksempel:
Timing-Allow-Origin: * (Tillader enhver oprindelse at se timing-detaljerne)
Timing-Allow-Origin: https://www.your-website.com (Tillader kun din hjemmeside)
Dette er afgørende, når du arbejder med dine egne CDN'er eller API'er på forskellige subdomæner. Sørg for, at de er konfigureret til at sende TAO-headeren, så du kan få fuld ydeevnesynlighed.
Browserunderstøttelse
Resource Timing API, inklusive `PerformanceObserver`, er bredt understøttet på tværs af alle moderne browsere (Chrome, Firefox, Safari, Edge). For ældre browsere er den dog muligvis ikke tilgængelig. Indpak altid din kode i en `try...catch`-blok eller tjek for eksistensen af `window.PerformanceObserver`, før du bruger den, for at undgå fejl på ældre klienter.
Konklusion: Fra Data til Beslutninger
Resource Timing API er et essentielt instrument i den moderne webudviklers værktøjskasse. Det afmystificerer netværksvandfaldet og leverer de rå, granulære data, der er nødvendige for at bevæge sig fra vage klager om, at "siden er langsom" til præcise, datadrevne diagnoser som "vores tredjeparts chat-widget har en TTFB på 400ms for brugere i Sydøstasien."
Ved at udnytte `PerformanceObserver` til at indsamle rigtige brugerdata og analysere den fulde livscyklus for hver ressource, kan du:
- Stille tredjepartsudbydere til ansvar for deres ydeevne.
- Validere effektiviteten af dine CDN- og caching-strategier over hele kloden.
- Finde og rette overdimensionerede billeder og uoptimerede aktiver.
- Korrelere frontend-forsinkelser med backend-behandlingstider.
Rejsen mod et hurtigere web er kontinuerlig. Start i dag. Åbn din browsers udviklerkonsol, kør kodestykkerne fra denne artikel på din egen side, og begynd at udforske de rige ydeevnedata, der har ventet på dig hele tiden. Ved at måle det, der betyder noget, kan du bygge hurtigere, mere robuste og mere behagelige oplevelser for alle dine brugere, uanset hvor de er i verden.