Ontdek hoe de Performance Observer API een krachtige, niet-intrusieve manier biedt om runtime webprestaties te monitoren, Core Web Vitals bij te houden en de gebruikerservaring te optimaliseren.
Webprestaties ontsluiten: een diepe duik in de Performance Observer API
In de snelle digitale wereld van vandaag zijn webprestaties geen luxe; het is een noodzaak. Een trage of niet-reagerende website kan leiden tot frustratie bij de gebruiker, hogere bouncepercentages en een directe negatieve impact op bedrijfsdoelen, of dat nu verkoop, advertentie-inkomsten of gebruikersbetrokkenheid is. Jarenlang hebben ontwikkelaars vertrouwd op tools die prestaties op een enkel moment meten, meestal tijdens het initiële laden van de pagina. Hoewel nuttig, mist deze aanpak een cruciaal deel van het verhaal: de volledige ervaring van de gebruiker terwijl ze met de pagina interageren. Dit is waar runtime prestatiemonitoring om de hoek komt kijken, en de krachtigste tool hiervoor is de Performance Observer API.
Traditionele methoden omvatten vaak het ophalen van prestatiegegevens met functies zoals performance.getEntries(). Dit kan inefficiënt zijn, gevoelig voor het missen van cruciale gebeurtenissen die tussen peilingen plaatsvinden, en kan zelfs bijdragen aan de prestatieoverhead die het probeert te meten. De Performance Observer API revolutioneert dit proces door een asynchroon, low-overhead mechanisme te bieden om je te abonneren op prestatiegebeurtenissen terwijl ze plaatsvinden. Deze gids neemt je mee op een diepe duik in deze essentiële API en laat je zien hoe je de kracht ervan kunt benutten om Core Web Vitals te monitoren, knelpunten te identificeren en uiteindelijk snellere, aangenamere webervaringen te bouwen voor een wereldwijd publiek.
Wat is de Performance Observer API?
In de kern is de Performance Observer API een interface die een manier biedt om gebeurtenissen voor prestatiemetingen, bekend als prestatie-items, te observeren en te verzamelen. Beschouw het als een speciale luisteraar voor prestatie-gerelateerde activiteiten binnen de browser. In plaats van dat je de browser actief vraagt: 'Is er al iets gebeurd?', vertelt de browser je proactief: 'Er heeft zojuist een nieuwe prestatiegebeurtenis plaatsgevonden! Hier zijn de details.'
Dit wordt bereikt door middel van een observer-patroon. Je maakt een observer-instantie, vertelt deze welke soorten prestatiegebeurtenissen je interesseren (bijv. grote paints, gebruikersinvoer, layout verschuivingen) en geeft een callback-functie op. Wanneer er een nieuwe gebeurtenis van een specifiek type wordt geregistreerd in de prestatielijn van de browser, wordt je callback-functie aangeroepen met een lijst van de nieuwe items. Dit asynchrone, push-gebaseerde model is veel efficiënter en betrouwbaarder dan het oudere pull-gebaseerde model van herhaaldelijk aanroepen van performance.getEntries().
De oude manier versus de nieuwe manier
Laten we, om de innovatie van Performance Observer te waarderen, de twee benaderingen contrasteren:
- De oude manier (Polling): Je zou setTimeout of requestAnimationFrame kunnen gebruiken om periodiek performance.getEntriesByName('my-metric') aan te roepen om te zien of je meting is geregistreerd. Dit is problematisch omdat je te laat kunt controleren en de gebeurtenis kunt missen, of te vaak kunt controleren en CPU-cycli kunt verspillen. Je loopt ook het risico de prestatiebuffer van de browser te vullen als je de items niet regelmatig leegmaakt.
- De nieuwe manier (Observeren): Je stelt een PerformanceObserver eenmalig in. Deze zit rustig op de achtergrond en verbruikt minimale bronnen. Zodra een relevant prestatie-item is geregistreerd - of het nu een milliseconde na het laden van de pagina is of tien minuten in de sessie van een gebruiker - wordt je code direct op de hoogte gesteld. Dit zorgt ervoor dat je nooit een gebeurtenis mist en dat je monitoringcode zo efficiënt mogelijk is.
Waarom je Performance Observer zou moeten gebruiken
Het integreren van de Performance Observer API in je ontwikkelingsworkflow biedt een veelheid aan voordelen die cruciaal zijn voor moderne webapplicaties die een wereldwijd bereik nastreven.
- Niet-intrusieve monitoring: De callback van de observer wordt doorgaans uitgevoerd tijdens inactieve perioden, waardoor wordt gegarandeerd dat je prestatiebewakingscode de gebruikerservaring niet stoort of de hoofdthread blokkeert. Het is ontworpen om lichtgewicht te zijn en een verwaarloosbare prestatievoetafdruk te hebben.
- Uitgebreide runtime-gegevens: Het web is dynamisch. Prestatieproblemen doen zich niet alleen voor bij het laden. Een gebruiker kan een complexe animatie activeren, meer inhoud laden door te scrollen of interactie hebben met een zwaar onderdeel, lang nadat de initiële pagina is geladen. Performance Observer legt deze runtime-gebeurtenissen vast en geeft je een compleet beeld van de hele gebruikerssessie.
- Toekomstbestendig en gestandaardiseerd: Het is de W3C-aanbevolen standaard voor het verzamelen van prestatiegegevens. Nieuwe prestatiemetingen en API's zijn ontworpen om ermee te integreren, waardoor het een duurzame en toekomstgerichte keuze is voor je projecten.
- De basis van Real User Monitoring (RUM): Om echt te begrijpen hoe je site presteert voor gebruikers in verschillende landen, op verschillende apparaten en netwerkomstandigheden, heb je gegevens van echte sessies nodig. Performance Observer is de ideale tool voor het bouwen van een robuuste RUM-oplossing, waarmee je essentiële statistieken kunt verzamelen en naar een analyseservice kunt sturen voor aggregatie en analyse.
- Elimineert race-condities: Met polling kun je proberen toegang te krijgen tot een prestatie-item voordat het is geregistreerd. Het observatiemodel elimineert deze race-conditie volledig, aangezien je code pas wordt uitgevoerd nadat het item beschikbaar is.
Aan de slag: de basis van Performance Observer
Het gebruik van de API is eenvoudig. Het proces omvat drie hoofdstappen: een observer maken, een callback definiëren en de observer vertellen waar hij naar moet kijken.
1. Een observer maken met een callback
Eerst instantiëer je een PerformanceObserver-object en geef je het een callback-functie mee. Deze functie wordt uitgevoerd wanneer nieuwe items worden gedetecteerd.
const observer = new PerformanceObserver((entryList) => { for (const entry of entryList.getEntries()) { console.log('Entry Type:', entry.entryType); console.log('Entry Name:', entry.name); console.log('Start Time:', entry.startTime); console.log('Duration:', entry.duration); } });
De callback ontvangt een PerformanceObserverEntryList-object. Je kunt de getEntries()-methode op deze lijst aanroepen om een array te krijgen van alle nieuw geobserveerde prestatie-items.
2. Specifieke itemtypes observeren
Een observer doet niets totdat je hem vertelt wat hij moet monitoren. Dit doe je met behulp van de methode .observe(). Deze methode accepteert een object met een eigenschap entryTypes (of in sommige moderne gevallen alleen type voor een enkel type), wat een array is van strings die de prestatie-itemtypes vertegenwoordigen waarin je geïnteresseerd bent.
// Begin met observeren van twee typen items observer.observe({ entryTypes: ['mark', 'measure'] });
Enkele van de meest voorkomende itemtypes zijn:
- 'resource': Details over netwerkverzoeken voor assets zoals scripts, afbeeldingen en stylesheets.
- 'paint': Timing voor first-paint en first-contentful-paint.
- 'largest-contentful-paint': De Core Web Vital-metriek voor waargenomen laadsnelheid.
- 'layout-shift': De Core Web Vital-metriek voor visuele stabiliteit.
- 'first-input': Informatie over de eerste gebruikersinteractie, gebruikt voor de First Input Delay Core Web Vital.
- 'longtask': Identificeert taken in de hoofdthread die langer duren dan 50 milliseconden, wat kan leiden tot een gebrek aan responsiviteit.
- 'mark' & 'measure': Aangepaste markers en metingen die je in je eigen code definieert met behulp van de User Timing API.
3. De observer stoppen
Wanneer je geen gegevens meer hoeft te verzamelen, is het een goede gewoonte om de observer te verbreken om bronnen vrij te maken.
observer.disconnect();
Praktische use-cases: Core Web Vitals monitoren
Core Web Vitals zijn een set specifieke factoren die Google belangrijk vindt in de algehele gebruikerservaring van een webpagina. Het monitoren ervan is een van de krachtigste toepassingen van de Performance Observer API. Laten we eens kijken hoe je ze kunt meten.
Largest Contentful Paint (LCP) monitoren
LCP meet de laadprestaties. Het markeert het punt in de tijdlijn van het laden van de pagina waarop de hoofdinhoud waarschijnlijk is geladen. Een goede LCP-score is 2,5 seconden of minder.
Het LCP-element kan veranderen terwijl de pagina wordt geladen. In eerste instantie kan een kop het LCP-element zijn, maar later kan een grotere afbeelding worden geladen en het nieuwe LCP-element worden. Daarom is een Performance Observer perfect: hij informeert je over elke potentiële LCP-kandidaat terwijl deze wordt weergegeven.
// Observeer LCP en registreer de uiteindelijke waarde let lcpValue = 0; const lcpObserver = new PerformanceObserver((entryList) => { const entries = entryList.getEntries(); // Het laatste item is de meest actuele LCP-kandidaat const lastEntry = entries[entries.length - 1]; lcpValue = lastEntry.startTime; console.log(`LCP bijgewerkt: ${lcpValue.toFixed(2)}ms`, lastEntry.element); }); lcpObserver.observe({ type: 'largest-contentful-paint', buffered: true }); // Het is een goede gewoonte om de observer te verbreken nadat de gebruiker interactie heeft gehad, // omdat interacties kunnen voorkomen dat nieuwe LCP-kandidaten worden verzonden. // window.addEventListener('beforeunload', () => lcpObserver.disconnect());
Let op het gebruik van buffered: true. Dit is een cruciale optie die de observer instrueert om items op te nemen die zijn geregistreerd *voordat* de observe()-methode werd aangeroepen. Dit voorkomt dat je een vroege LCP-gebeurtenis mist.
First Input Delay (FID) en Interaction to Next Paint (INP) monitoren
Deze statistieken meten de interactiviteit. Ze kwantificeren de ervaring van de gebruiker wanneer ze voor het eerst proberen te interageren met de pagina.
First Input Delay (FID) meet de tijd vanaf het moment dat een gebruiker voor het eerst interactie heeft met een pagina (bijv. op een knop klikt) tot het moment waarop de browser daadwerkelijk in staat is om event handlers te verwerken als reactie op die interactie. Een goede FID is 100 milliseconden of minder.
Interaction to Next Paint (INP) is een nieuwere, uitgebreidere metriek die FID heeft vervangen als een Core Web Vital in maart 2024. Terwijl FID alleen de *vertraging* van de *eerste* interactie meet, beoordeelt INP de *totale latentie* van *alle* gebruikersinteracties gedurende de levenscyclus van de pagina en rapporteert de slechtste. Dit geeft een beter beeld van de algehele responsiviteit. Een goede INP is 200 milliseconden of minder.
Je kunt FID monitoren met behulp van het 'first-input' itemtype:
// Observeer FID const fidObserver = new PerformanceObserver((entryList) => { for (const entry of entryList.getEntries()) { const fid = entry.processingStart - entry.startTime; console.log(`FID: ${fid.toFixed(2)}ms`); // Verbreek de verbinding nadat de eerste invoer is gerapporteerd fidObserver.disconnect(); } }); fidObserver.observe({ type: 'first-input', buffered: true });
Het monitoren van INP is iets ingewikkelder omdat het naar de volledige duur van een gebeurtenis kijkt. Je observeert het 'event' itemtype en berekent de duur, waarbij je de langste bijhoudt.
// Vereenvoudigd INP-monitoringvoorbeeld let worstInp = 0; const inpObserver = new PerformanceObserver((entryList) => { for (const entry of entryList.getEntries()) { // De INP is de duur van de gebeurtenis const inp = entry.duration; // We geven alleen om interacties die langer duren dan de huidige slechtste if (inp > worstInp) { worstInp = inp; console.log(`Nieuwe slechtste INP: ${worstInp.toFixed(2)}ms`); } } }); inpObserver.observe({ type: 'event', durationThreshold: 16, buffered: true }); // durationThreshold helpt bij het filteren van zeer korte, waarschijnlijk onbeduidende gebeurtenissen.
Cumulative Layout Shift (CLS) monitoren
CLS meet de visuele stabiliteit. Het helpt om te kwantificeren hoe vaak gebruikers onverwachte lay-outverschuivingen ervaren - een frustrerende ervaring waarbij de inhoud zonder waarschuwing op de pagina beweegt. Een goede CLS-score is 0,1 of minder.
De score is een aggregatie van alle individuele lay-outverschuivingsscores. Een Performance Observer is hier essentieel, omdat deze elke verschuiving rapporteert terwijl deze plaatsvindt.
// Observeer en bereken de totale CLS-score let clsScore = 0; const clsObserver = new PerformanceObserver((entryList) => { for (const entry of entryList.getEntries()) { // We willen geen verschuivingen meetellen die zijn veroorzaakt door gebruikersinvoer if (!entry.hadRecentInput) { clsScore += entry.value; console.log(`Huidige CLS-score: ${clsScore.toFixed(4)}`); } } }); clsObserver.observe({ type: 'layout-shift', buffered: true });
De eigenschap hadRecentInput is belangrijk. Het helpt je om legitieme lay-outverschuivingen te filteren die optreden als reactie op een actie van een gebruiker (zoals het klikken op een knop die een menu uitklapt), die niet moeten meetellen voor de CLS-score.
Naast Core Web Vitals: andere krachtige itemtypen
Hoewel Core Web Vitals een geweldig startpunt zijn, kan Performance Observer veel meer monitoren. Hier zijn een paar andere ongelooflijk nuttige itemtypen.
Long Tasks bijhouden (`longtask`)
De Long Tasks API maakt taken zichtbaar die de hoofdthread gedurende 50 milliseconden of langer bezetten. Deze zijn problematisch, want terwijl de hoofdthread bezet is, kan de pagina niet reageren op gebruikersinvoer, wat leidt tot een trage of bevroren ervaring. Het identificeren van deze taken is essentieel voor het verbeteren van INP.
// Observeer long tasks const longTaskObserver = new PerformanceObserver((entryList) => { for (const entry of entryList.getEntries()) { console.log(`Long Task Gedetecteerd: ${entry.duration.toFixed(2)}ms`); // De eigenschap 'attribution' kan je soms vertellen wat de long task heeft veroorzaakt console.log('Attribution:', entry.attribution); } }); longTaskObserver.observe({ type: 'longtask', buffered: true });
Resource timings analyseren (`resource`)
Het begrijpen hoe je assets worden geladen, is essentieel voor het afstemmen van prestaties. Het 'resource'-itemtype geeft je gedetailleerde netwerktiminggegevens voor elke resource op je pagina, inclusief DNS-opzoeking, TCP-verbinding en downloadtijden van inhoud.
// Observeer resourcetimings const resourceObserver = new PerformanceObserver((entryList) => { for (const entry of entryList.getEntries()) { // Laten we langzaam ladende afbeeldingen vinden if (entry.initiatorType === 'img' && entry.duration > 500) { console.warn(`Trage afbeelding gedetecteerd: ${entry.name}`, `Duur: ${entry.duration.toFixed(2)}ms`); } } }); // 'buffered: true' gebruiken is bijna altijd nodig voor resourcetimings // om assets te vangen die zijn geladen voordat dit script werd uitgevoerd. resourceObserver.observe({ type: 'resource', buffered: true });
Aangepaste prestatiemarkeringen meten (`mark` en `measure`)
Soms moet je de prestaties van toepassingsspecifieke logica meten. De User Timing API stelt je in staat om aangepaste tijdstempels te maken en de duur daartussen te meten.
- performance.mark('start-operation'): Creëert een tijdstempel met de naam 'start-operation'.
- performance.mark('end-operation'): Creëert een andere tijdstempel.
- performance.measure('my-operation', 'start-operation', 'end-operation'): Creëert een meting tussen de twee markeringen.
Performance Observer kan luisteren naar deze aangepaste 'mark'- en 'measure'-items, wat perfect is voor het verzamelen van timinggegevens over zaken als component render tijden in een JavaScript-framework of de duur van een kritieke API-aanroep en de daaropvolgende gegevensverwerking.
// In je applicatiecode: performance.mark('start-data-processing'); // ... een complexe gegevensverwerking ... performance.mark('end-data-processing'); performance.measure('data-processing-duration', 'start-data-processing', 'end-data-processing'); // In je monitoring script: const customObserver = new PerformanceObserver((entryList) => { for (const entry of entryList.getEntriesByName('data-processing-duration')) { console.log(`Aangepaste meting '${entry.name}': ${entry.duration.toFixed(2)}ms`); } }); customObserver.observe({ entryTypes: ['measure'] });
Geavanceerde concepten en best practices
Om de Performance Observer API effectief te gebruiken in een professionele productieomgeving, kun je deze best practices in overweging nemen.
- Overweeg altijd `buffered: true`: Voor itemtypen die vroeg in het laden van de pagina kunnen voorkomen (zoals 'resource', 'paint' of 'largest-contentful-paint'), is het essentieel om de gebufferde vlag te gebruiken om te voorkomen dat je ze mist.
- Controleer op browserondersteuning: Hoewel breed ondersteund in moderne browsers, is het altijd verstandig om te controleren op het bestaan ervan voordat je het gebruikt. Je kunt ook controleren welke itemtypen worden ondersteund door een specifieke browser.
- if ('PerformanceObserver' in window && PerformanceObserver.supportedEntryTypes.includes('longtask')) { // Veilig om PerformanceObserver te gebruiken voor long tasks }
- Verzend gegevens naar een analyseservice: Gegevens loggen in de console is geweldig voor ontwikkeling, maar voor monitoring in de echte wereld moet je deze gegevens aggregeren. De beste manier om deze telemetrie van de client te verzenden, is met behulp van de navigator.sendBeacon() API. Het is een niet-blokkerend mechanisme dat is ontworpen voor het verzenden van kleine hoeveelheden gegevens naar een server, en het werkt betrouwbaar, zelfs wanneer een pagina wordt ontladen.
- Groepeer observers per probleem: Hoewel je een enkele observer kunt gebruiken voor meerdere itemtypen, is het vaak schoner om afzonderlijke observers te maken voor verschillende problemen (bijvoorbeeld één voor Core Web Vitals, één voor resourcetimings, één voor aangepaste statistieken). Dit verbetert de leesbaarheid en onderhoudbaarheid van de code.
- Begrijp de prestatieoverhead: De API is ontworpen om een zeer lage overhead te hebben. Een zeer complexe callback-functie die zware berekeningen uitvoert, kan echter mogelijk de prestaties beïnvloeden. Houd je observer-callbacks slank en efficiënt. Stel alle zware verwerking uit naar een web worker of stuur de onbewerkte gegevens naar je backend om daar te worden verwerkt.
Conclusie: een prestatie-eerste cultuur bouwen
De Performance Observer API is meer dan alleen een andere tool; het is een fundamentele verschuiving in de manier waarop we webprestaties benaderen. Het brengt ons van reactieve, eenmalige metingen naar proactieve, continue monitoring die de ware, dynamische ervaring van onze gebruikers over de hele wereld weerspiegelt. Door een betrouwbare en efficiënte manier te bieden om Core Web Vitals, lange taken, resourcetimings en aangepaste statistieken vast te leggen, stelt het ontwikkelaars in staat om prestatieknelpunten te identificeren en op te lossen voordat ze een aanzienlijk aantal gebruikers beïnvloeden.
Het adopteren van de Performance Observer API is een cruciale stap in de richting van het bouwen van een prestatie-eerste cultuur in elk ontwikkelingsteam. Wanneer je kunt meten wat belangrijk is, kun je verbeteren wat belangrijk is. Begin vandaag nog met het integreren van deze observers in je projecten. Je gebruikers - waar ze zich ook ter wereld bevinden - zullen je bedanken voor de snellere, soepelere en aangenamere ervaring.