API voor lange animatieframes

De Long Animation Frames API (LoAF - uitgesproken als Lo-Af) is een update van de Long Tasks API om een ​​beter begrip te bieden van langzame updates van de gebruikersinterface (UI). Dit kan handig zijn om langzame animatieframes te identificeren die waarschijnlijk van invloed zijn op de Interaction to Next Paint (INP) Core Web Vital-metriek die de responsiviteit meet, of om andere UI-janks te identificeren die de vloeiendheid beïnvloeden.

Status van de API

Browserondersteuning

  • 123
  • 123
  • X
  • X

Bron

Na een origin-proefversie van Chrome 116 naar Chrome 122 is de LoAF API verscheept vanuit Chrome 123 .

De API voor lange taken

Browserondersteuning

  • 58
  • 79
  • X
  • X

Bron

De Long Animation Frames API is een alternatief voor de Long Tasks API die al enige tijd beschikbaar is in Chrome (sinds Chrome 58). Zoals de naam al doet vermoeden, kunt u met de Long Task API toezicht houden op lange taken, dit zijn taken die de hoofdreeks 50 milliseconden of langer in beslag nemen. Lange taken kunnen worden gemonitord met behulp van de PerformanceLongTaskTiming interface, met een PeformanceObserver :

const observer = new PerformanceObserver((list) => {
  console.log(list.getEntries());
});

observer.observe({ type: 'longtask', buffered: true });

Lange taken veroorzaken waarschijnlijk reactieproblemen. Als een gebruiker probeert te communiceren met een pagina (bijvoorbeeld door op een knop te klikken of een menu te openen) maar de hoofdthread al bezig is met een lange taak, wordt de interactie van de gebruiker uitgesteld totdat die taak is voltooid.

Om het reactievermogen te verbeteren, wordt vaak geadviseerd om lange taken op te splitsen . Als elke lange taak in plaats daarvan wordt opgedeeld in een reeks van meerdere, kleinere taken, kan het mogelijk zijn dat belangrijkere taken tussendoor worden uitgevoerd om aanzienlijke vertragingen bij het reageren op interacties te voorkomen.

Wanneer we de responsiviteit proberen te verbeteren, bestaat de eerste poging dus vaak uit het uitvoeren van een prestatietracering en het bekijken van lange taken. Dit kan via een laboratoriumgebaseerde audittool zoals Lighthouse (die een audit voor lange hoofdthreadtaken heeft), of door naar lange taken te kijken in Chrome DevTools .

Testen in een laboratorium is vaak een slecht startpunt voor het identificeren van problemen met het reactievermogen , omdat deze tools mogelijk geen interacties omvatten; als ze dat wel doen, vormen ze slechts een kleine subset van waarschijnlijke interacties. Idealiter meet je de oorzaken van langzame interacties in het veld.

Tekortkomingen van de Long Tasks API

Het meten van lange taken in het veld met behulp van een Performance Observer is slechts enigszins nuttig. In werkelijkheid geeft het niet zoveel informatie behalve het feit dat er een lange taak heeft plaatsgevonden en hoe lang deze heeft geduurd.

Real User Monitoring (RUM)-tools gebruiken dit vaak om het aantal of de duur van lange taken te bepalen of om te identificeren op welke pagina's ze plaatsvinden, maar zonder de onderliggende details van wat de lange taak veroorzaakte, is dit slechts van beperkt nut. De Long Tasks API heeft alleen een basisattributiemodel , dat u in het beste geval alleen vertelt in welke container de lange taak plaatsvond (het document op het hoogste niveau of een <iframe> ), maar niet het script of de functie die de taak heeft aangeroepen, zoals blijkt uit een typische invoer:

{
  "name": "unknown",
  "entryType": "longtask",
  "startTime": 31.799999997019768,
  "duration": 136,
  "attribution": [
    {
      "name": "unknown",
      "entryType": "taskattribution",
      "startTime": 0,
      "duration": 0,
      "containerType": "window",
      "containerSrc": "",
      "containerId": "",
      "containerName": ""
    }
  ]
}

De Long Tasks API is ook een onvolledige weergave, omdat deze ook enkele belangrijke taken kan uitsluiten. Sommige updates, zoals het renderen, vinden plaats in afzonderlijke taken die idealiter samen met de voorgaande uitvoering zouden moeten worden opgenomen, waardoor die update het 'totale werk' voor die interactie nauwkeurig meet. Voor meer details over de beperkingen van het vertrouwen op taken, zie de sectie 'Waar lange taken tekortschieten' van de uitleg .

Het laatste probleem is dat het meten van lange taken alleen rapporteert over individuele taken die langer duren dan de limiet van 50 milliseconden. Een animatieframe kan bestaan ​​uit verschillende taken onder deze limiet van 50 milliseconden, maar toch gezamenlijk het weergavevermogen van de browser blokkeren.

De API voor lange animatieframes

Browserondersteuning

  • 123
  • 123
  • X
  • X

Bron

De Long Animation Frames API (LoAF) is een nieuwe API die een aantal tekortkomingen van de Long Tasks API probeert aan te pakken, zodat ontwikkelaars meer bruikbare inzichten kunnen krijgen om responsiviteitsproblemen aan te pakken en INP te verbeteren.

Een goede responsiviteit betekent dat een pagina snel reageert op interacties die ermee plaatsvinden. Dat houdt in dat we alle updates die de gebruiker nodig heeft, tijdig kunnen schilderen en kunnen voorkomen dat deze updates worden geblokkeerd. Voor INP wordt aanbevolen om binnen 200 milliseconden of minder te reageren , maar voor andere updates (bijvoorbeeld animaties) kan zelfs dat te lang zijn.

De Long Animation Frames API is een alternatieve benadering voor het meten van blokkeringswerk. In plaats van de afzonderlijke taken te meten, meet de Long Animation Frames API, zoals de naam al doet vermoeden, lange animatieframes . Er is sprake van een lang animatieframe wanneer een weergave-update meer dan 50 milliseconden wordt vertraagd (hetzelfde als de drempelwaarde voor de Long Tasks API).

Lange animatieframes kunnen op dezelfde manier worden waargenomen als lange taken met een PerformanceObserver , maar in plaats daarvan kijken we naar het type long-animation-frame :

const observer = new PerformanceObserver((list) => {
  console.log(list.getEntries());
});

observer.observe({ type: 'long-animation-frame', buffered: true });

Eerdere lange animatieframes kunnen ook als volgt uit de prestatietijdlijn worden opgevraagd:

const loafs = performance.getEntriesByType('long-animation-frame');

Er is echter een maxBufferSize voor prestatie-items, waarna nieuwere items worden verwijderd, dus de PerformanceObserver-aanpak is de aanbevolen aanpak. De buffergrootte long-animation-frame is ingesteld op 200, hetzelfde als voor long-tasks .

Voordelen van het kijken naar frames in plaats van naar taken

Het belangrijkste voordeel van het bekijken hiervan vanuit een frameperspectief in plaats van vanuit een takenperspectief, is dat een lange animatie kan bestaan ​​uit een willekeurig aantal taken die cumulatief hebben geresulteerd in een lang animatieframe. Dit betreft het laatste punt dat eerder werd genoemd, waarbij de som van vele kleinere, render-blokkerende taken vóór een animatieframe mogelijk niet naar boven komt door de Long Tasks API.

Een bijkomend voordeel van deze alternatieve kijk op lange taken is de mogelijkheid om timing-uitsplitsingen van het hele frame te bieden. In plaats van alleen een startTime en een duration op te nemen, zoals de Long Tasks API, bevat LoAF een veel gedetailleerder overzicht van de verschillende delen van de frameduur, waaronder:

  • startTime : de starttijd van het lange animatieframe ten opzichte van de starttijd van de navigatie.
  • duration : de duur van het lange animatieframe (exclusief presentatietijd).
  • renderStart : de starttijd van de weergavecyclus, inclusief callbacks requestAnimationFrame , berekening van stijl en lay-out, callbacks van waarnemers van formaat wijzigen en intersectiewaarnemers.
  • styleAndLayoutStart : het begin van de tijdsperiode besteed aan stijl- en lay-outberekeningen.
  • firstUIEventTimestamp : de tijd van de eerste UI-gebeurtenis (muis/toetsenbord enzovoort) die in de loop van dit frame moet worden afgehandeld.
  • blockingDuration : de duur in milliseconden waarvoor het animatieframe werd geblokkeerd.

Met deze tijdstempels kan het lange animatieframe worden opgedeeld in timings:

Tijdstip Berekening
Starttijd startTime
Eindtijd startTime + duration
Werkduur renderStart ? renderStart - startTime : duration
Renderduur renderStart ? (startTime + duration) - renderStart: 0
Render: duur vóór de lay-out styleAndLayoutStart ? styleAndLayoutStart - renderStart : 0
Render: Stijl- en lay-outduur styleAndLayoutStart ? (startTime + duration) - styleAndLayoutStart : 0

Voor meer details over deze individuele timings raadpleegt u de uitleg , die gedetailleerde details biedt over welke activiteit bijdraagt ​​aan een lang animatieframe.

Betere toeschrijving

Het invoertype long-animation-frame bevat betere attributiegegevens van elk script dat heeft bijgedragen aan een lang animatieframe.

Net als bij de Long Tasks API wordt dit geleverd in een reeks attributie-items, waarvan elk de volgende details bevat:

  • Een name en EntryType retourneren beide script .
  • Een betekenisvolle invoker , die aangeeft hoe het script is aangeroepen (bijvoorbeeld 'IMG#id.onload' , 'Window.requestAnimationFrame' of 'Response.json.then' ).
  • Het invokerType van het scriptingangspunt:
    • user-callback : Een bekende callback die is geregistreerd via een webplatform-API (bijvoorbeeld setTimeout , requestAnimationFrame ).
    • event-listener : Een luisteraar naar een platformgebeurtenis (bijvoorbeeld click , load , keyup ).
    • resolve-promise : Handler van een platformbelofte (bijvoorbeeld fetch() . Merk op dat in het geval van beloften alle handlers van dezelfde beloften samen worden gemengd als één "script") .
    • reject-promise : Volgens resolve-promise , maar voor de afwijzing.
    • classic-script : Scriptevaluatie (bijvoorbeeld <script> of import() )
    • module-script : Hetzelfde als classic-script , maar voor modulescripts.
  • Afzonderlijke timinggegevens voor dat script:
    • startTime : Tijd waarop de invoerfunctie werd aangeroepen.
    • duration : De duur tussen startTime en het moment waarop de daaropvolgende microtaakwachtrij klaar is met verwerken.
    • executionStart : De tijd na het compileren.
    • forcedStyleAndLayoutDuration : De totale tijd die is besteed aan het verwerken van de geforceerde lay-out/stijl binnen deze functie (zie thrashing ).
    • pauseDuration : de totale tijd die is besteed aan het "pauzeren" van synchrone bewerkingen (waarschuwing, synchrone XHR).
  • Details van de scriptbron:
    • sourceURL : De naam van de scriptbron, indien beschikbaar (of leeg als deze niet wordt gevonden).
    • sourceFunctionName : De naam van de scriptfunctie, indien beschikbaar (of leeg als deze niet wordt gevonden).
    • sourceCharPosition : De positie van het scriptteken, indien beschikbaar (of -1 indien niet gevonden).
  • windowAttribution : De container (het document op het hoogste niveau, of een <iframe> ) waarin het lange animatieframe plaatsvond.
  • window : Een verwijzing naar hetzelfde venster.

Waar beschikbaar, kunnen ontwikkelaars dankzij de bronvermeldingen precies weten hoe elk script in het lange animatieframe is aangeroepen, tot aan de tekenpositie in het aanroepende script. Dit geeft de exacte locatie in een JavaScript-bron die resulteerde in het lange animatieframe.

Voorbeeld van een prestatie-invoer met een long-animation-frame

Een compleet voorbeeld van een prestatie long-animation-frame , dat één enkel script bevat, is:

{
  "blockingDuration": 0,
  "duration": 60,
  "entryType": "long-animation-frame",
  "firstUIEventTimestamp": 11801.099999999627,
  "name": "long-animation-frame",
  "renderStart": 11858.800000000745,
  "scripts": [
    {
      "duration": 45,
      "entryType": "script",
      "executionStart": 11803.199999999255,
      "forcedStyleAndLayoutDuration": 0,
      "invoker": "DOMWindow.onclick",
      "invokerType": "event-listener",
      "name": "script",
      "pauseDuration": 0,
      "sourceURL": "https://web.dev/js/index-ffde4443.js",
      "sourceFunctionName": "myClickHandler",
      "sourceCharPosition": 17796,
      "startTime": 11803.199999999255,
      "window": [Window object],
      "windowAttribution": "self"
    }
  ],
  "startTime": 11802.400000000373,
  "styleAndLayoutStart": 11858.800000000745
}

Zoals u kunt zien, levert dit een ongekende hoeveelheid gegevens op voor websites om de oorzaak van laggy rendering-updates te kunnen begrijpen.

De API voor lange animatieframes inschakelen

De Long Animation Frames API is standaard ingeschakeld vanaf Chrome 123.

Met behulp van de Long Animation Frames API in het veld

Tools als Lighthouse zijn – hoewel nuttig voor het ontdekken en reproduceren van problemen – laboratoriumtools die mogelijk belangrijke aspecten van de gebruikerservaring missen die alleen veldgegevens kunnen bieden. De Long Animation Frames API kan in het veld worden gebruikt om belangrijke contextuele gegevens te verzamelen voor gebruikersinteracties die de Long Tasks API niet kon. Dit kan u helpen problemen met interactiviteit aan het licht te brengen en te reproduceren die u anders misschien niet had ontdekt.

Hierna worden enkele voorgestelde strategieën vermeld, maar het Chrome-team wil graag feedback horen over deze API en hoe ontwikkelaars en RUM-providers zichzelf zouden zien met behulp van de informatie die de API biedt.

Functie voor het detecteren van API-ondersteuning voor lange animatieframes

U kunt de volgende code gebruiken om te testen of de API wordt ondersteund:

if (PerformanceObserver.supportedEntryTypes.includes('long-animation-frame')) {
  // Monitor LoAFs
}

In dit geval kan het volgende alternatief worden gebruikt, terwijl lange animatieframes nog niet standaard worden ondersteund en zich in deze overgangsstatus bevinden:

if ('PerformanceLongAnimationFrameTiming' in window) {
  // Monitor LoAFs
}

Lange animatiegegevens terugrapporteren naar een analyse-eindpunt

Zoals u ziet, bevat de LoAF-prestatie-invoer waardevolle informatie. Eén strategie zou zijn om alle LoAF's te monitoren en degenen boven een bepaalde drempel terug te sturen naar een analyse-eindpunt voor latere analyse:

const REPORTING_THRESHOLD_MS = 150;

const observer = new PerformanceObserver(list => {
  for (const entry of list.getEntries()) {
    if (entry.duration > REPORTING_THRESHOLD_MS) {
      // Example here logs to console, but could also report back to analytics
      console.log(entry);
    }
  }
});
observer.observe({ type: 'long-animation-frame', buffered: true });

Omdat de lange animatieframe-items behoorlijk groot kunnen zijn, moeten ontwikkelaars beslissen welke gegevens uit het item naar Analytics moeten worden gestuurd. Bijvoorbeeld de samenvattingstijden van de invoer en misschien de scriptnamen, of een andere minimale reeks andere contextuele gegevens die mogelijk noodzakelijk worden geacht.

Het observeren van de ergste lange animatieframes

Sites willen mogelijk gegevens verzamelen over het langste animatieframe (of frames) om de hoeveelheid gegevens die moet worden beaconed te verminderen. Dus ongeacht hoeveel lange animatieframes een pagina ervaart, worden alleen de gegevens voor de slechtste, vijf of hoeveel lange animatieframes absoluut noodzakelijk zijn teruggevonden.

MAX_LOAFS_TO_CONSIDER = 10;
let longestBlockingLoAFs = [];

const observer = new PerformanceObserver(list => {
  longestBlockingLoAFs = longestBlockingLoAFs.concat(list.getEntries()).sort(
    (a, b) => b.blockingDuration - a.blockingDuration
  ).slice(0, MAX_LOAFS_TO_CONSIDER);
});
observer.observe({ type: 'long-animation-frame', buffered: true });

Op het juiste moment ( idealiter tijdens de visibilitychange ) keert u terug naar analyses. Voor lokaal testen kunt u periodiek console.table gebruiken:

console.table(longestBlockingLoAFs);

Koppeling naar de langste INP-interactie

Als uitbreiding op het observeren van de slechtste LoAF's kunnen de LoAF-frames die overeenkomen met de INP-invoer worden gebruikt als attributiegegevens om verdere details te geven over hoe INP kan worden verbeterd.

Er is momenteel geen directe API om een ​​INP-item te koppelen aan de bijbehorende LoAF-item(s) , hoewel het mogelijk is om dit in code te doen door de begin- en eindtijden van elk te vergelijken (zie dit voorbeeldscript ).

Rapporteren van lange animatieframes met interacties

Een alternatieve aanpak waarvoor minder code nodig is, is om altijd de grootste (of bovenste X grootste) LoAF-vermeldingen te verzenden waar een interactie plaatsvond tijdens het frame (wat kan worden gedetecteerd door de aanwezigheid van een firstUIEventTimestamp waarde). In de meeste gevallen omvat dit de INP-interactie voor een bepaald bezoek, en in zeldzame gevallen waarin dit niet het geval is, worden er nog steeds lange interacties weergegeven die belangrijk zijn om op te lossen, omdat dit de INP-interactie voor andere gebruikers kan zijn.

De volgende code registreert alle LoAF-vermeldingen van meer dan 150 milliseconden waarbij een interactie plaatsvond tijdens het frame. De 150 is hier gekozen omdat deze iets minder is dan de "goede" INP-drempel van 200 milliseconden. Afhankelijk van uw behoeften kunt u een hogere of lagere waarde kiezen.

const REPORTING_THRESHOLD_MS = 150;

const observer = new PerformanceObserver(list => {
    for (const entry of list.getEntries()) {
      if (entry.duration > REPORTING_THRESHOLD_MS &&
        entry.firstUIEventTimestamp > 0
      ) {
        // Example here logs to console, but could also report back to analytics
        console.log(entry);
      }
    }
});
observer.observe({ type: 'long-animation-frame', buffered: true });

Gemeenschappelijke patronen identificeren in lange animatieframes

Een alternatieve strategie zou zijn om te kijken naar veelgebruikte scripts die het meest voorkomen in lange animatieframe-items. Gegevens kunnen worden teruggekoppeld op script- en/of karakterpositieniveau om recidivisten te identificeren.

Dit kan met name goed werken voor aanpasbare platforms waar thema's of plug-ins die prestatieproblemen veroorzaken, gemakkelijker op een aantal sites kunnen worden geïdentificeerd.

De uitvoeringstijd van veelgebruikte scripts (of de oorsprong van derden) in lange animatieframes kan worden samengevat en gerapporteerd om gemeenschappelijke bijdragers aan lange animatieframes op een site of een verzameling sites te identificeren. Als u bijvoorbeeld naar URL's wilt kijken:

const observer = new PerformanceObserver(list => {
  const allScripts = list.getEntries().flatMap(entry => entry.scripts);
  const scriptSource = [...new Set(allScripts.map(script => script.sourceURL))];
  const scriptsBySource= scriptSource.map(sourceURL => ([sourceURL,
      allScripts.filter(script => script.sourceURL === sourceURL)
  ]));
  const processedScripts = scriptsBySource.map(([sourceURL, scripts]) => ({
    sourceURL,
    count: scripts.length,
    totalDuration: scripts.reduce((subtotal, script) => subtotal + script.duration, 0)
  }));
  processedScripts.sort((a, b) => b.totalDuration - a.totalDuration);
  // Example here logs to console, but could also report back to analytics
  console.table(processedScripts);
});

observer.observe({type: 'long-animation-frame', buffered: true});

En een voorbeeld van deze uitvoer is:

(index) sourceURL count totalDuration
0 'https://example.consent.com/consent.js' 1 840
1 'https://example.com/js/analytics.js' 7 628
2 'https://example.chatapp.com/web-chat.js' 1 5

De API voor lange animatieframes gebruiken in tooling

De API zou ook extra ontwikkelaarstools voor lokaal foutopsporing mogelijk kunnen maken. Hoewel sommige tools zoals Lighthouse en Chrome DevTools veel van deze gegevens hebben kunnen verzamelen met behulp van traceringsdetails op een lager niveau, zou het hebben van deze API op een hoger niveau ervoor kunnen zorgen dat andere tools toegang krijgen tot deze gegevens.

Gegevens van lange animatieframes zichtbaar maken in DevTools

U kunt lange animatieframes in DevTools weergeven met behulp van de performance.measure() API, die vervolgens worden weergegeven in de DevTools-gebruikerstimingstrack in prestatiesporen om aan te geven waar u zich op moet richten voor prestatieverbeteringen:

const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    performance.measure('LoAF', {
      start: entry.startTime,
      end: entry.startTime + entry.duration,
    });
  }
});

observer.observe({ type: 'long-animation-frame', buffered: true });

Als deze API op de lange termijn nuttig blijkt, zal deze waarschijnlijk in DevTools zelf worden opgenomen, maar dankzij het vorige codefragment kan deze in de tussentijd daar worden opgedoken.

Gegevens van lange animatieframes gebruiken in andere ontwikkelaarstools

De Web Vitals-extensie heeft de waarde laten zien van het loggen van samenvattende foutopsporingsinformatie om prestatieproblemen te diagnosticeren. Nu de API is gelanceerd, kunnen dergelijke tools gemakkelijker gegevens boven water halen om ontwikkelaars bewust te maken waar ze hun inspanningen moeten concentreren. We zijn ook van plan dit toe te voegen aan de Web Vitals JavaScript-bibliotheek in versie 4.

Gegevens van lange animatieframes gebruiken in geautomatiseerde testtools

Op soortgelijke wijze kunnen geautomatiseerde testtools, misschien in CI/CD-pijplijnen, details over potentiële prestatieproblemen aan het licht brengen door lange animatieframes te meten terwijl verschillende testsuites worden uitgevoerd.

FAQ

Enkele van de veelgestelde vragen over deze API zijn:

Waarom niet gewoon de Long Tasks API uitbreiden of herhalen?

Dit is een alternatieve kijk op het rapporteren van een vergelijkbare – maar uiteindelijk andere – meting van potentiële problemen met het reactievermogen. Het is belangrijk om ervoor te zorgen dat sites die afhankelijk zijn van de bestaande Long Tasks API blijven functioneren om te voorkomen dat bestaande gebruiksscenario's worden verstoord.

Hoewel de Long Tasks API kan profiteren van enkele kenmerken van LoAF (zoals een beter attributiemodel), zijn wij van mening dat het focussen op frames in plaats van taken veel voordelen biedt die dit een fundamenteel andere API maken dan de bestaande Long Tasks API.

Zal dit de Long Tasks API vervangen?

Hoewel we van mening zijn dat de Long Animation Frames API een betere, completere API is voor het meten van lange taken, zijn er op dit moment geen plannen om de Long Tasks API te beëindigen.

Feedback gewenst

Feedback kan worden gegeven op de GitHub Issues-lijst , of bugs in de Chrome-implementatie van de API kunnen worden opgeslagen in de issue tracker van Chrome .

Conclusie

De Long Animation Frames API is een opwindende nieuwe API met veel potentiële voordelen ten opzichte van de vorige Long Tasks API.

Het blijkt een belangrijk instrument te zijn voor het aanpakken van responsiviteitsproblemen, zoals gemeten door INP. INP is een uitdagende statistiek om te optimaliseren en deze API is een manier waarop het Chrome-team het identificeren en aanpakken van problemen voor ontwikkelaars eenvoudiger wil maken.

De reikwijdte van de Long Animation Frames API reikt echter verder dan alleen INP en kan helpen bij het identificeren van andere oorzaken van trage updates die de algehele soepelheid van de gebruikerservaring van een website kunnen beïnvloeden.

Dankbetuigingen

Miniatuurafbeelding door Henry Be op Unsplash .