BemÀstra Resource Timing API för att diagnostisera och optimera prestanda för frontend. LÀr dig mÀta varje resurs laddningstid, frÄn DNS-uppslag till nedladdning.
LÄs upp prestanda för frontend: En djupdykning i Resource Timing API
I webbutvecklingens vÀrld Àr hastighet inte bara en funktion; det Àr ett grundlÀggande krav för en positiv anvÀndarupplevelse. En lÄngsamt laddande webbplats kan leda till högre avvisningsfrekvens, lÀgre anvÀndarengagemang och i slutÀndan en negativ inverkan pÄ affÀrsmÄlen. Medan verktyg som Lighthouse och WebPageTest ger ovÀrderlig diagnostik pÄ hög nivÄ, representerar de ofta ett enskilt, syntetiskt test. För att verkligen förstÄ och optimera prestanda för en global publik behöver vi mÀta upplevelsen för riktiga anvÀndare, pÄ deras enheter, pÄ deras nÀtverk. Det Àr hÀr Real User Monitoring (RUM) kommer in, och ett av dess mest kraftfulla verktyg Àr Resource Timing API.
Denna omfattande guide tar dig med pÄ en djupdykning i Resource Timing API. Vi kommer att utforska vad det Àr, hur man anvÀnder det och hur man omvandlar dess granulÀra data till handlingsbara insikter som dramatiskt kan förbÀttra din applikations laddningsprestanda. Oavsett om du Àr en erfaren frontend-utvecklare eller precis har pÄbörjat din resa med prestandaoptimering, kommer denna artikel att utrusta dig med kunskapen för att dissekera och förstÄ nÀtverksprestandan för varje enskild tillgÄng pÄ din sida.
Vad Àr Resource Timing API?
Resource Timing API Àr ett webblÀsarbaserat JavaScript-API som tillhandahÄller detaljerad nÀtverkstidsdata för varje resurs som en webbsida laddar ner. TÀnk pÄ det som en mikroskopisk lins för din sidas nÀtverksaktivitet. För varje bild, skript, stilmall, typsnitt och API-anrop (via `fetch` eller `XMLHttpRequest`) fÄngar detta API en högupplöst tidsstÀmpel för varje steg i nÀtverksbegÀran.
Det Àr en del av en större svit av Performance API:er, som arbetar tillsammans för att ge en holistisk bild av din applikations prestanda. Medan Navigation Timing API fokuserar pÄ huvuddokumentets livscykel, zoomar Resource Timing API in pÄ alla de beroende resurser som huvuddokumentet begÀr.
Varför Àr det sÄ viktigt?
- Granularitet: Det gÄr bortom ett enskilt "sidladdningstid"-mÄtt. Du kan se exakt hur lÄng tid DNS-uppslag, TCP-anslutning och nedladdning av innehÄll tog för ett specifikt tredjepartsskript eller en kritisk "hero image".
- Data frÄn riktiga anvÀndare: Till skillnad frÄn labb-baserade verktyg körs detta API i dina anvÀndares webblÀsare. Detta gör att du kan samla in prestandadata frÄn ett brett spektrum av nÀtverksförhÄllanden, enheter och geografiska platser, vilket ger dig en sann bild av din globala anvÀndarupplevelse.
- Handlingsbara insikter: Genom att analysera denna data kan du peka ut specifika flaskhalsar. Ăr ett tredjepartsanalysskript lĂ„ngsamt att ansluta? Underpresterar ditt CDN i en viss region? Ăr dina bilder för stora? Resource Timing API ger de bevis som behövs för att besvara dessa frĂ„gor med sĂ€kerhet.
Anatomin av en resursladdning: En genomgÄng av tidslinjen
KÀrnan i Resource Timing API Àr `PerformanceResourceTiming`-objektet. För varje resurs som laddas skapar webblÀsaren ett av dessa objekt, som innehÄller en mÀngd information om tid och storlek. För att förstÄ dessa objekt Àr det hjÀlpsamt att visualisera laddningsprocessen som ett vattenfallsdiagram, dÀr varje steg följer det föregÄende.
LÄt oss gÄ igenom de viktigaste egenskaperna hos ett `PerformanceResourceTiming`-objekt. Alla tidsvÀrden Àr högupplösta tidsstÀmplar mÀtta i millisekunder frÄn starten av sidnavigeringen (`performance.timeOrigin`).
startTime -> fetchStart -> domainLookupStart -> domainLookupEnd -> connectStart -> connectEnd -> requestStart -> responseStart -> responseEnd
Viktiga tidsegenskaper
name: URL:en till resursen. Detta Àr din primÀra identifierare.entryType: En strÀng som indikerar typen av prestandapost. För vÄra ÀndamÄl kommer detta alltid att vara "resource".initiatorType: Detta Àr otroligt anvÀndbart för felsökning. Det talar om för dig hur resursen begÀrdes. Vanliga vÀrden inkluderar 'img', 'link' (för CSS), 'script', 'css' (för resurser som laddas inifrÄn CSS som `@import`), 'fetch' och 'xmlhttprequest'.duration: Den totala tiden för resursen, berÀknad somresponseEnd - startTime. Detta Àr det övergripande mÄttet för en enskild resurs.startTime: TidsstÀmpeln omedelbart innan resursinhÀmtningen pÄbörjas.fetchStart: TidsstÀmpeln precis innan webblÀsaren börjar hÀmta resursen. Den kan kontrollera cacheminnen (HTTP-cache, Service Worker-cache) innan den fortsÀtter till nÀtverket. Om resursen serveras frÄn en cache kommer mÄnga av de efterföljande tidsvÀrdena att vara noll.domainLookupStart&domainLookupEnd: Dessa markerar starten och slutet pÄ DNS-uppslaget (Domain Name System). Varaktigheten (domainLookupEnd - domainLookupStart) Àr tiden det tog att översÀtta domÀnnamnet till en IP-adress. Ett högt vÀrde hÀr kan tyda pÄ en lÄngsam DNS-leverantör.connectStart&connectEnd: Dessa markerar starten och slutet pÄ att etablera en anslutning till servern. För HTTP Àr detta TCP-trevÀgshandskakningen. Varaktigheten (connectEnd - connectStart) Àr din TCP-anslutningstid.secureConnectionStart: Om resursen laddas över HTTPS, markerar denna tidsstÀmpel början pÄ SSL/TLS-handskakningen. Varaktigheten (connectEnd - secureConnectionStart) talar om hur lÄng tid krypteringsförhandlingen tog. LÄngsamma TLS-handskakningar kan vara ett tecken pÄ felkonfiguration av servern eller nÀtverkslatens.requestStart: TidsstÀmpeln precis innan webblÀsaren skickar den faktiska HTTP-begÀran för resursen till servern. Tiden mellanconnectEndochrequestStartkallas ofta för "request queuing"-tid, dÀr webblÀsaren vÀntar pÄ en tillgÀnglig anslutning.responseStart: TidsstÀmpeln nÀr webblÀsaren tar emot den allra första byten av svaret frÄn servern. Varaktigheten (responseStart - requestStart) Àr den berömda Time to First Byte (TTFB). En hög TTFB Àr nÀstan alltid en indikator pÄ en lÄngsam backend-process eller latens pÄ serversidan.responseEnd: TidsstÀmpeln nÀr den sista byten av resursen har mottagits, vilket framgÄngsrikt avslutar begÀran. Varaktigheten (responseEnd - responseStart) representerar nedladdningstiden för innehÄllet.
Egenskaper för resursstorlek
Att förstÄ resursstorlek Àr lika viktigt som att förstÄ tid. API:et tillhandahÄller tre nyckelmÄtt:
transferSize: Storleken i bytes pÄ resursen som överförts över nÀtverket, inklusive headers och den komprimerade svarskroppen. Om resursen serverades frÄn en cache kommer detta ofta att vara 0. Detta Àr siffran som direkt pÄverkar anvÀndarens dataabonnemang och nÀtverkstid.encodedBodySize: Storleken i bytes pÄ payload-kroppen *efter* komprimering (t.ex. Gzip eller Brotli) men *före* dekomprimering. Detta hjÀlper dig att förstÄ storleken pÄ sjÀlva payloaden, separat frÄn headers.decodedBodySize: Storleken i bytes pÄ payload-kroppen i sin okomprimerade, ursprungliga form. Att jÀmföra detta medencodedBodySizeavslöjar effektiviteten av din komprimeringsstrategi. Om dessa tvÄ siffror ligger mycket nÀra varandra för en textbaserad tillgÄng (som JS, CSS eller HTML), fungerar din komprimering troligen inte korrekt.
Server Timing
En av de mest kraftfulla integrationerna med Resource Timing API Àr egenskapen `serverTiming`. Din backend kan skicka prestandamÄtt i en speciell HTTP-header (`Server-Timing`), och dessa mÄtt kommer att visas i `serverTiming`-arrayen pÄ motsvarande `PerformanceResourceTiming`-objekt. Detta överbryggar klyftan mellan prestandaövervakning för frontend och backend, vilket gör att du kan se databasfrÄgetider eller API-bearbetningsfördröjningar direkt i din frontend-data.
Till exempel kan en backend skicka denna header:
Server-Timing: db;dur=53, api;dur=47.2, cache;desc="HIT"
Denna data skulle vara tillgÀnglig i `serverTiming`-egenskapen, vilket gör att du kan korrelera en hög TTFB med en specifik lÄngsam process pÄ backend.
Hur man kommer Ät Resource Timing-data i JavaScript
Nu nÀr vi förstÄr den tillgÀngliga datan, lÄt oss titta pÄ de praktiska sÀtten att samla in den med JavaScript. Det finns tvÄ primÀra metoder.
Metod 1: `performance.getEntriesByType('resource')`
Detta Àr det enklaste sÀttet att komma igÄng. Denna metod returnerar en array av alla `PerformanceResourceTiming`-objekt för resurser som redan har laddats fÀrdigt pÄ sidan vid tidpunkten för anropet.
// VÀnta tills sidan har laddats för att sÀkerstÀlla att de flesta resurser fÄngas upp
window.addEventListener('load', () => {
const resources = performance.getEntriesByType('resource');
resources.forEach((resource) => {
console.log(`Resurs laddad: ${resource.name}`);
console.log(` - Total tid: ${resource.duration.toFixed(2)}ms`);
console.log(` - Initiator: ${resource.initiatorType}`);
console.log(` - Ăverföringsstorlek: ${resource.transferSize} bytes`);
});
});
BegrÀnsning: Denna metod Àr en ögonblicksbild. Om du anropar den för tidigt missar du resurser som Ànnu inte har laddats. Om din applikation dynamiskt laddar resurser lÄngt efter den initiala sidladdningen, skulle du behöva polla denna metod upprepade gÄnger, vilket Àr ineffektivt.
Metod 2: `PerformanceObserver` (Den rekommenderade metoden)
`PerformanceObserver` Àr ett mer modernt, robust och högpresterande sÀtt att samla in prestandaposter. IstÀllet för att du pollar efter data, skickar webblÀsaren nya poster till din observer-callback nÀr de blir tillgÀngliga.
HÀr Àr varför den Àr bÀttre:
- Asynkron: Den blockerar inte huvudtrÄden.
- Omfattande: Den kan fÄnga poster frÄn allra första början av sidladdningen och undviker race conditions dÀr ett skript körs efter att en resurs redan har laddats.
- Effektiv: Den undviker behovet av att polla med `setTimeout` eller `setInterval`.
HÀr Àr en standardimplementation:
try {
const observer = new PerformanceObserver((list) => {
list.getEntries().forEach((entry) => {
// Bearbeta varje resurspost nÀr den kommer in
if (entry.entryType === 'resource') {
console.log(`Resurs observerad: ${entry.name}`);
console.log(` - Time to First Byte (TTFB): ${(entry.responseStart - entry.requestStart).toFixed(2)}ms`);
}
});
});
// Börja observera 'resource'-poster.
// 'buffered'-flaggan sÀkerstÀller att vi fÄr poster som laddades innan vÄr observer skapades.
observer.observe({ type: 'resource', buffered: true });
// Du kan sluta observera senare om det behövs
// observer.disconnect();
} catch (e) {
console.error('PerformanceObserver stöds inte i denna webblÀsare.');
}
Alternativet buffered: true Àr kritiskt. Det talar om för observatören att omedelbart skicka alla `resource`-poster som redan finns i webblÀsarens prestandabuffer, vilket sÀkerstÀller att du fÄr en komplett lista frÄn början.
Hantera prestandabufferten
WebblÀsare har en standardgrÀns för hur mÄnga resurs-timing-poster de lagrar (vanligtvis 150). PÄ mycket komplexa sidor kan denna buffer bli full. NÀr det hÀnder avfyrar webblÀsaren en `resourcetimingbufferfull`-hÀndelse, och inga nya poster lÀggs till.
Du kan hantera detta genom att:
- Ăka bufferstorleken: AnvĂ€nd `performance.setResourceTimingBufferSize(limit)` för att sĂ€tta en högre grĂ€ns, till exempel 300.
- Rensa bufferten: AnvÀnd `performance.clearResourceTimings()` efter att du har bearbetat posterna för att göra plats för nya.
performance.addEventListener('resourcetimingbufferfull', () => {
console.warn('Resource Timing-bufferten Àr full. Rensar...');
// Bearbeta befintliga poster frÄn din observer först
// Rensa sedan bufferten
performance.clearResourceTimings();
// Du kan behöva justera bufferstorleken igen om detta hÀnder ofta
// performance.setResourceTimingBufferSize(500);
});
Praktiska anvÀndningsfall och handlingsbara insikter
Att samla in data Àr bara det första steget. Det verkliga vÀrdet ligger i att omvandla den datan till handlingsbara förbÀttringar. LÄt oss utforska nÄgra vanliga prestandaproblem och hur Resource Timing API hjÀlper dig att lösa dem.
AnvÀndningsfall 1: Identifiera lÄngsamma tredjepartsskript
Problemet: Tredjepartsskript för analys, reklam, kundsupport-widgets och A/B-testning Àr ökÀnda prestandabovar. De kan vara lÄngsamma att ladda, blockera rendering och till och med orsaka instabilitet.
Lösningen: AnvÀnd Resource Timing API för att isolera och mÀta pÄverkan av dessa skript pÄ dina riktiga anvÀndare.
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Àtt ett tröskelvÀrde, t.ex. 200ms
console.warn(`LÄngsamt tredjepartsskript upptÀckt: ${script.name}`, {
duration: `${script.duration.toFixed(2)}ms`,
transferSize: `${script.transferSize} bytes`
});
// I ett riktigt RUM-verktyg skulle du skicka denna data till din analys-backend.
}
});
});
observer.observe({ type: 'resource', buffered: true });
Handlingsbara insikter:
- LÄng varaktighet: Om ett skript konsekvent har en lÄng varaktighet, övervÀg om det verkligen Àr nödvÀndigt. Kan dess funktionalitet ersÀttas med ett mer högpresterande alternativ?
- Laddningsstrategi: Laddas skriptet synkront? AnvÀnd attributen `async` eller `defer` pÄ `<script>`-taggen för att förhindra att det blockerar sidans rendering.
- Ladda selektivt: Kan skriptet laddas villkorligt, endast pÄ de sidor dÀr det Àr absolut nödvÀndigt?
AnvÀndningsfall 2: Optimera bildleverans
Problemet: Stora, ooptimerade bilder Àr en av de vanligaste orsakerna till lÄngsamma sidladdningar, sÀrskilt pÄ mobila enheter med begrÀnsad bandbredd.
Lösningen: Filtrera resursposter efter `initiatorType: 'img'` och analysera deras storlek och laddningstider.
// ... inuti en PerformanceObserver-callback ...
list.getEntries()
.filter(entry => entry.initiatorType === 'img')
.forEach(image => {
const downloadTime = image.responseEnd - image.responseStart;
// En stor bild kan ha en hög nedladdningstid och en stor transferSize
if (downloadTime > 500 || image.transferSize > 100000) { // 500ms eller 100KB
console.log(`Potentiellt problem med stor bild: ${image.name}`, {
downloadTime: `${downloadTime.toFixed(2)}ms`,
transferSize: `${(image.transferSize / 1024).toFixed(2)} KB`
});
}
});
Handlingsbara insikter:
- Hög `transferSize` och `downloadTime`: Detta Àr en tydlig signal om att bilden Àr för stor. Optimera den genom att anvÀnda moderna format som WebP eller AVIF, komprimera den lÀmpligt och Àndra storlek pÄ den till dess visade dimensioner.
- AnvÀnd `srcset`: Implementera responsiva bilder med `srcset`-attributet för att servera olika bildstorlekar baserat pÄ anvÀndarens viewport.
- Lazy Loading: För bilder "below the fold" (utanför den synliga delen av sidan), anvÀnd `loading="lazy"` för att skjuta upp deras laddning tills anvÀndaren rullar dem i sikte.
AnvÀndningsfall 3: Diagnostisera nÀtverksflaskhalsar
Problemet: Ibland Àr problemet inte sjÀlva resursen utan nÀtverksvÀgen till den. LÄngsam DNS, latenta anslutningar eller överbelastade servrar kan alla försÀmra prestandan.
Lösningen: Bryt ner `duration` i dess bestÄndsdelar för att peka ut kÀllan till fördröjningen.
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(`Analys för ${resource.name}`);
if (dnsTime > 50) console.warn(` - Hög DNS-tid: ${dnsTime.toFixed(2)}ms`);
if (tcpTime > 100) console.warn(` - Hög TCP-anslutningstid: ${tcpTime.toFixed(2)}ms`);
if (ttfb > 300) console.warn(` - Hög TTFB (lÄngsam server): ${ttfb.toFixed(2)}ms`);
if (downloadTime > 500) console.warn(` - LÄngsam nedladdning av innehÄll: ${downloadTime.toFixed(2)}ms`);
}
// ... anropa analyzeNetworkPhases(entry) inuti din observer ...
Handlingsbara insikter:
- Hög DNS-tid: Din DNS-leverantör kan vara lĂ„ngsam. ĂvervĂ€g att byta till en snabbare global leverantör. Du kan ocksĂ„ anvĂ€nda `` för att lösa DNS för kritiska tredjepartsdomĂ€ner i förvĂ€g.
- Hög TCP-tid: Detta indikerar latens vid etablering av anslutningen. Ett Content Delivery Network (CDN) kan minska detta genom att servera tillgÄngar frÄn en plats geografiskt nÀrmare anvÀndaren. Att anvÀnda `` kan utföra bÄde DNS-uppslag och TCP-handskakning tidigt.
- Hög TTFB: Detta pekar pÄ en lÄngsam backend. Arbeta med ditt backend-team för att optimera databasfrÄgor, förbÀttra cachning pÄ serversidan eller uppgradera serverhÄrdvara. `Server-Timing`-headern Àr din bÀsta vÀn hÀr.
- Hög nedladdningstid: Detta Àr en funktion av resursstorlek och nÀtverksbandbredd. Optimera tillgÄngen (komprimera, minifiera) eller anvÀnd ett CDN för att förbÀttra genomströmningen.
BegrÀnsningar och att tÀnka pÄ
Ăven om det Ă€r otroligt kraftfullt har Resource Timing API nĂ„gra viktiga begrĂ€nsningar att vara medveten om.
Resurser frÄn andra domÀner och `Timing-Allow-Origin`-headern
Av sÀkerhetsskÀl begrÀnsar webblÀsare de tidsdetaljer som Àr tillgÀngliga för resurser som laddas frÄn en annan origin (domÀn, protokoll eller port) Àn din huvudsida. Som standard, för en cross-origin-resurs, kommer de flesta tidsegenskaper som `redirectStart`, `domainLookupStart`, `connectStart`, `requestStart`, `responseStart` och storleksegenskaper som `transferSize` att vara noll.
För att exponera dessa detaljer mÄste servern som hostar resursen inkludera HTTP-headern `Timing-Allow-Origin` (TAO). Till exempel:
Timing-Allow-Origin: * (TillÄter vilken origin som helst att se tidsdetaljerna)
Timing-Allow-Origin: https://www.your-website.com (TillÄter endast din webbplats)
Detta Àr avgörande nÀr du arbetar med dina egna CDN:er eller API:er pÄ olika subdomÀner. Se till att de Àr konfigurerade att skicka TAO-headern sÄ att du kan fÄ full prestandasynlighet.
WebblÀsarstöd
Resource Timing API, inklusive `PerformanceObserver`, har brett stöd i alla moderna webblÀsare (Chrome, Firefox, Safari, Edge). För Àldre webblÀsare kan det dock saknas. Omslut alltid din kod i ett `try...catch`-block eller kontrollera förekomsten av `window.PerformanceObserver` innan du anvÀnder den för att undvika fel pÄ Àldre klienter.
Slutsats: FrÄn data till beslut
Resource Timing API Àr ett oumbÀrligt instrument i den moderna webbutvecklarens verktygslÄda. Det avmystifierar nÀtverksvattenfallet och tillhandahÄller de rÄa, granulÀra data som behövs för att gÄ frÄn vaga klagomÄl om att "sidan Àr lÄngsam" till precisa, datadrivna diagnoser som "vÄr tredjeparts-chattwidget har en TTFB pÄ 400ms för anvÀndare i Sydostasien."
Genom att utnyttja `PerformanceObserver` för att samla in data frÄn riktiga anvÀndare och analysera den fullstÀndiga livscykeln för varje resurs kan du:
- HÄlla tredjepartsleverantörer ansvariga för deras prestanda.
- Validera effektiviteten av dina CDN- och cachningsstrategier över hela vÀrlden.
- Hitta och ÄtgÀrda överdimensionerade bilder och ooptimerade tillgÄngar.
- Korrelera fördröjningar i frontend med bearbetningstider i backend.
Resan mot en snabbare webb Ă€r kontinuerlig. Börja idag. Ăppna din webblĂ€sares utvecklarkonsol, kör kodavsnitten frĂ„n den hĂ€r artikeln pĂ„ din egen webbplats och börja utforska den rika prestandadata som har vĂ€ntat pĂ„ dig hela tiden. Genom att mĂ€ta det som betyder nĂ„got kan du bygga snabbare, mer motstĂ„ndskraftiga och mer njutbara upplevelser för alla dina anvĂ€ndare, var de Ă€n befinner sig i vĂ€rlden.