Interfejs API Long Animation Frames

Long Animation Frames API (LoAF – wymawiane Lo-Af) to aktualizacja Long Tasks API, która pozwala lepiej zrozumieć powolne aktualizacje interfejsu użytkownika. Może to być przydatne do identyfikowania powolnych klatek animacji, które prawdopodobnie wpłyną na podstawowy wskaźnik interakcji z następnym wyrenderowaniem (INP), który mierzy czas reagowania, lub do zidentyfikowania innych zakłóceń interfejsu, które wpływają na płynność.

Stan interfejsu API

Obsługa przeglądarek

  • 123
  • x
  • x
  • x

W ramach testów origin z Chrome 116 na Chrome 122 interfejs LoAF API został wysłany z Chrome 123.

Interfejs API Long Tasks

Obsługa przeglądarek

  • 58
  • 79
  • x
  • x

Źródło

Interfejs Long Animation Frames API jest alternatywą dla interfejsu Long Tasks API, który jest już dostępny w Chrome (od Chrome 58). Jak sama nazwa wskazuje, interfejs Long Task API umożliwia monitorowanie długich zadań, czyli takich, które zajmują wątek główny przez co najmniej 50 milisekund. Długie zadania można monitorować za pomocą interfejsu PerformanceLongTaskTiming za pomocą PeformanceObserver:

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

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

Długie zadania mogą powodować problemy z reagowaniem. Jeśli użytkownik spróbuje wejść w interakcję ze stroną – na przykład kliknąć przycisk lub otworzyć menu – a wątek główny zajmie się już obszernym zadaniem, interakcja użytkownika będzie opóźniona i będzie czekać na jego zakończenie.

Aby poprawić czas reagowania, często zalecane jest podzielenie długich zadań. Jeśli natomiast każde długie zadanie zostanie podzielone na kilka mniejszych zadań, może to pozwolić na wykonywanie między nimi ważniejszych zadań, co pozwoli uniknąć znacznych opóźnień w reagowaniu na interakcje.

Gdy próbujesz poprawić czas reagowania, pierwszym działaniem jest często uruchomienie śledzenia wydajności i sprawdzanie długich zadań. Możesz na przykład użyć narzędzia kontrolnego opartego na laboratorium, takiego jak Lighthouse (które obejmuje kontrolę Unikaj długich zadań w wątku głównym), lub sprawdzić długie zadania w Narzędziach deweloperskich w Chrome.

Testy w laboratorium często nie są dobrym punktem wyjścia do identyfikowania problemów z responsywnością, ponieważ narzędzia te mogą nie uwzględniać interakcji, gdy już tak się dzieje, stanowią tylko niewielką część prawdopodobnych interakcji. Najlepiej mierzyć przyczyny powolnych interakcji w terenie.

Wady interfejsu Long Tasks API

Pomiar długich zadań w terenie za pomocą obserwatora wydajności jest tylko nieco przydatny. W rzeczywistości nie daje to aż tyle informacji, oprócz tego, że tak długo miało miejsce i ile czasu zajęło.

Narzędzia monitorowania użytkowników (Real User Monitoring, RUM) często wykorzystują te dane, aby określić trendy w zakresie liczby i czasu trwania długich zadań oraz określić, na których stronach się one pojawiają. Jednak bez informacji o czynnościach, które spowodowały tak długie zadanie, mamy do czynienia jedynie z ograniczonym wykorzystaniem. Interfejs Long Tasks API ma tylko podstawowy model atrybucji, który informuje tylko kontener, w którym miało miejsce długie zadanie (w dokumencie najwyższego poziomu lub <iframe>), ale nie ma skryptu lub funkcji, które je wywołały, jak widać w typowym wpisie:

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

Widok interfejsu Long Tasks API również jest niekompletny, ponieważ może także wykluczać niektóre ważne zadania. Niektóre aktualizacje, np. renderowanie, odbywają się w oddzielnych zadaniach, które najlepiej, gdyby były uwzględnione w połączeniu z poprzednim wykonaniem, aby umożliwić dokładny pomiar „całkowitej pracy” danej interakcji. Więcej informacji o ograniczeniach polegania na zadaniach znajdziesz w sekcji wyjaśniającej, gdzie długie zadania nie spełniają warunków.

Ostatni problem polega na tym, że pomiary długich zadań są rejestrowane tylko w przypadku poszczególnych zadań, które trwają dłużej niż limit 50 milisekund. Ramka animacji może składać się z kilku zadań, które nie przekraczają tego limitu 50 milisekund, ale razem nadal blokują zdolność przeglądarki do renderowania.

Interfejs API Long Animation Frames

Obsługa przeglądarek

  • 123
  • x
  • x
  • x

Long Animation Frames API (LoAF) to nowy interfejs API, który ma rozwiązać niektóre z niedoskonałości interfejsu Long Tasks API, umożliwiając deweloperom uzyskiwanie przydatnych statystyk pomagających w rozwiązywaniu problemów z czasem reakcji i poprawianiu INP.

Dobra responsywność oznacza, że strona szybko reaguje na interakcje z nią. Oznacza to możliwość szybkiego zainstalowania wszelkich potrzebnych użytkownikowi aktualizacji i unikanie blokowania tych aktualizacji. W przypadku INP zalecamy czas reakcji w ciągu maksymalnie 200 milisekund, ale w przypadku innych aktualizacji (np. animacji) czas ten może być zbyt długi.

Interfejs API Long Animation Frames to alternatywne podejście do pomiaru blokowania. Zamiast mierzyć poszczególne zadania, interfejs Long Animation Frames API – jak wskazuje jej nazwa – mierzy długie klatki animacji. Długa ramka animacji ma miejsce, gdy aktualizacja renderowania jest opóźniona o ponad 50 milisekund (tak samo jak wartość progowa w przypadku interfejsu Long Tasks API).

Długie klatki animacji można obserwować w podobny sposób jak w przypadku długich zadań za pomocą PerformanceObserver, ale na dane typu long-animation-frame:

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

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

Zapytania o poprzednie długie klatki animacji można też wysyłać z osi czasu wydajności, na przykład:

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

Występuje jednak maxBufferSize dla wpisów dotyczących wydajności, po których nowsze wpisy są pomijane, dlatego zaleca się podejście oparte na PerformanceObserver. Rozmiar bufora w long-animation-frame jest ustawiony na 200, tak jak w przypadku long-tasks.

Zalety wyświetlania ramek zamiast zadań

Główną zaletą spojrzenia na ten element z perspektywy klatki, a nie z perspektywy zadań, jest to, że długa animacja może składać się z dowolnej liczby działań, które łącznie utworzą długą klatkę animacji. To dotyczy ostatniego punktu wspomnianego wcześniej, w którym suma wielu mniejszych zadań blokujących renderowanie przed ramką animacji może nie zostać wyświetlona przez interfejs Long Tasks API.

Kolejną zaletą tego alternatywnego widoku w przypadku długich zadań jest możliwość wyświetlania podziałów czasowych całej klatki. Zamiast tylko parametrów startTime i duration, takich jak interfejs Long Tasks API, LoAF zawiera znacznie bardziej szczegółowe zestawienie różnych elementów czasu trwania klatki, w tym:

  • startTime: czas rozpoczęcia długiej klatki animacji w stosunku do czasu rozpoczęcia nawigacji.
  • duration: czas trwania długiej klatki animacji (bez czasu prezentacji).
  • renderStart: czas rozpoczęcia cyklu renderowania, który obejmuje wywołania zwrotne requestAnimationFrame, styl i układ, zmianę rozmiaru obserwatora i wywołania zwrotne obserwatora elementów interfejsu.
  • styleAndLayoutStart: początek przedziału czasu przy obliczaniu stylu i układu.
  • firstUIEventTimestamp: czas pierwszego zdarzenia interfejsu (myszy/klawiatury itd.) obsługiwanego w trakcie tej klatki.
  • blockingDuration: czas (w milisekundach), przez jaki klatka animacji była blokowana.

Te sygnatury czasowe umożliwiają podział długiej klatki animacji na czas:

Czas Obliczenie
Czas rozpoczęcia startTime
Czas zakończenia startTime + duration
Czas pracy renderStart ? renderStart - startTime : duration
Czas renderowania renderStart ? (startTime + duration) - renderStart: 0
Renderowanie: czas trwania wstępnego układu styleAndLayoutStart ? styleAndLayoutStart - renderStart : 0
Renderowanie: czas trwania stylu i układu styleAndLayoutStart ? (startTime + duration) - styleAndLayoutStart : 0

Więcej informacji o tych indywidualnych harmonogramach znajdziesz w wyjaśnieniu, w którym znajdziesz szczegółowe informacje o tym, która aktywność wpływa na długą klatkę animacji.

Lepsza atrybucja

Typ wpisu long-animation-frame zawiera dokładniejsze dane o atrybucji każdego skryptu, który przyczynił się do utworzenia długiej klatki animacji.

Podobnie jak w przypadku interfejsu Long Tasks API, ten parametr będzie dostępny w tablicy wpisów atrybucji, z których każdy zawiera następujące szczegóły:

  • Zarówno name, jak i EntryType zwracają wartość script.
  • Znaczące invoker wskazujące sposób wywołania skryptu (np. 'IMG#id.onload', 'Window.requestAnimationFrame' lub 'Response.json.then').
  • invokerType punktu wejścia skryptu:
    • user-callback: znane wywołanie zwrotne zarejestrowane przy użyciu interfejsu API platformy internetowej (np. setTimeout, requestAnimationFrame).
    • event-listener: odbiornik zdarzenia na platformie (np. click, load, keyup).
    • resolve-promise: moduł obsługi obietnicy platformy (np. fetch()). Zwróć uwagę, że w przypadku obietnic wszystkie moduły obsługi tych samych obietnic są mieszane jako jeden „skrypt”)..
    • reject-promise: zgodnie z resolve-promise, ale do odrzucenia.
    • classic-script: ocena skryptu (np. <script> lub import())
    • module-script: tak samo jak classic-script, ale dotyczy skryptów modułu.
  • Oddzielne dane o czasie dla tego skryptu:
    • startTime: czas wywołania funkcji wprowadzania.
    • duration: czas od startTime do zakończenia przetwarzania kolejnej kolejki mikrozadań.
    • executionStart: czas po kompilacji.
    • forcedStyleAndLayoutDuration: łączny czas przetwarzania wymuszonego układu/stylu w tej funkcji (patrz thrash).
    • pauseDuration: łączny czas „wstrzymywania” operacji synchronicznych (alert, synchroniczne XHR).
  • Szczegóły źródła skryptu:
    • sourceURL: nazwa zasobu skryptu, jeśli jest dostępna (lub pusta, jeśli nie została znaleziona).
    • sourceFunctionName: nazwa funkcji skryptu, jeśli jest dostępna (lub pusta, jeśli nie została znaleziona).
    • sourceCharPosition: pozycja znaku skryptu, jeśli jest dostępna (lub -1, jeśli nie istnieje).
  • windowAttribution: kontener (dokument najwyższego poziomu lub <iframe>), w którym wystąpiła długa klatka animacji.
  • window: odwołanie do okna z tej samej domeny.

Wpisy źródłowe pozwalają programistom dokładnie wiedzieć, jak został wywołany każdy skrypt w długiej ramce animacji, aż do pozycji znaku w skrypcie wywołującym. Wskazuje dokładną lokalizację zasobu JavaScript, która spowodowała utworzenie długiej klatki animacji.

Przykład wpisu o skuteczności: long-animation-frame

Kompletny przykład wpisu dotyczącego wydajności long-animation-frame, który zawiera pojedynczy skrypt, to:

{
  "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
}

Jak widać, dostarcza to bezprecedensowe ilości danych, dzięki którym witryny mogą zrozumieć przyczyny powolnych aktualizacji renderowania.

Włączanie interfejsu Long Animation Frames API

Interfejs Long Animation Frames API jest domyślnie włączony od Chrome 123.

Korzystanie w tej sytuacji z interfejsu API Long Animation Frames

Narzędzia takie jak Lighthouse – choć przydatne przy wykrywaniu i rozwiązaniu problemów – to narzędzia laboratoryjne, w których brakuje ważnych aspektów związanych z wrażeniami użytkownika, dostępnych tylko w przypadku danych terenowych. Interfejs API Long Animation Frames może służyć w terenie do gromadzenia ważnych danych kontekstowych związanych z interakcjami użytkowników, których interfejs Long Tasks API nie mógł tego zrobić. Dzięki temu możesz wykryć i odtworzyć problemy z interaktywnością, które w innym przypadku mogłyby nie być wykrywane.

W dalszej części opisujemy kilka sugerowanych strategii, ale zespół Chrome chętnie pozna opinie o tym interfejsie API i o tym, jak deweloperzy i dostawcy RUM mogliby zobaczyć je za pomocą udostępnianych przez niego informacji.

Obsługa interfejsu API Long Animation Frames

Za pomocą tego kodu możesz sprawdzić, czy interfejs API jest obsługiwany:

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

W tym przypadku można użyć następującej opcji alternatywnej, gdy długie klatki animacji nie są jeszcze domyślnie obsługiwane i są w tym stanie przejścia:

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

Raportowanie danych o długich animacjach z powrotem do punktu końcowego Analytics

Jak widać, wpis skuteczności LoAF zawiera cenne informacje. Jedną ze strategii jest monitorowanie wszystkich LoAF i przesyłanie tych, które przekraczają określony próg, z powrotem do punktu końcowego analityki w celu późniejszej analizy:

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 });

Długie pozycje klatki animacji mogą być dość duże, dlatego deweloperzy powinni zdecydować, jakie dane z takiego wpisu przesłać do analityki. Mogą to być na przykład skrócony czas wpisywania wpisu i nazwy skryptów lub inny minimalny zestaw innych danych kontekstowych, które mogą być konieczne.

Obserwowanie najgorszych długich klatek animacji

Witryny mogą chcieć gromadzić dane dotyczące najdłuższej klatki animacji (lub klatek), aby zmniejszyć ilość danych wymagających sygnalizowania po stronie serwera. Niezależnie od tego, ile długich klatek animacji wyświetla się na stronie, tylko dane o najgorszej, pięciu lub absolutnie potrzebnych długich klatkach animacji pojawiają się z powrotem.

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 });

W odpowiednim momencie (najlepiej w momencie zdarzenia visibilitychange) wróć do statystyk. Do testów lokalnych możesz okresowo używać usługi console.table:

console.table(longestBlockingLoAFs);

Łączenie z najdłuższą interakcją INP

W ramach obserwacji najgorszych wartości LoAF można wykorzystać ramki LoAF odpowiadające wpisowi INP jako dane atrybucji, aby uzyskać dodatkowe informacje na temat poprawy wartości INP.

Obecnie nie ma bezpośredniego interfejsu API powiązanego z wpisem INP z powiązanymi wpisami LoAF, ale można to zrobić w kodzie, porównując czas rozpoczęcia i zakończenia każdego z nich (zobacz ten przykładowy skrypt).

Zgłaszanie długich klatek animacji z interakcjami

Alternatywnym podejściem, które wymaga mniej kodu, jest wysyłanie zawsze największych (lub X największych) wpisów LoAF, w których miała miejsce interakcja w trakcie klatki (co można wykryć dzięki wartości firstUIEventTimestamp). W większości przypadków obejmuje to interakcję INP w przypadku danej wizyty. W rzadkich przypadkach powoduje także obserwację długich interakcji, które trzeba poprawić, ponieważ mogą to być interakcje INP w przypadku innych użytkowników.

Ten kod rejestruje wszystkie wpisy LoAF dłuższe niż 150 milisekund, w których interakcja miała miejsce w trakcie klatki. Wybieramy tutaj wartość 150, ponieważ jest ona nieco mniejsza niż próg „dobrego” INP, który wynosi 200 milisekund. W zależności od potrzeb możesz wybrać większą lub mniejszą wartość.

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 });

Rozpoznawanie typowych wzorców w długich klatkach animacji

Alternatywnym rozwiązaniem jest przyjrzenie się typowym skryptom, które występują najczęściej w elementach z długimi klatkami animacji. Dane można zgłaszać na poziomie skryptu lub na poziomie pozycji, aby umożliwić identyfikację osób wielokrotnie naruszających zasady.

Może się to sprawdzić szczególnie w przypadku platform z możliwością dostosowania, w przypadku których motywy lub wtyczki powodujące problemy z wydajnością można łatwo zidentyfikować w wielu witrynach.

Czas wykonywania popularnych skryptów (lub źródeł zewnętrznych) w długich klatkach animacji można podsumować i zsumować, aby zidentyfikować wspólne czynniki wpływające na długie klatki animacji w witrynie lub zbiorze witryn. Aby na przykład sprawdzić adresy URL:

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});

Przykładowe dane wyjściowe:

(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

Używanie interfejsu Long Animation Frames API w narzędziach

Interfejs API może też udostępniać dodatkowe narzędzia programistyczne do lokalnego debugowania. Chociaż niektóre narzędzia, takie jak Lighthouse i Narzędzia deweloperskie w Chrome, były w stanie zbierać duże ilości tych danych przy użyciu szczegółów śledzenia niższego poziomu, ale używanie interfejsu API wyższego poziomu może zapewnić innym narzędziom dostęp do tych danych.

Wyświetlanie danych długich klatek animacji w Narzędziach deweloperskich

Długie klatki animacji możesz wyświetlać w Narzędziach deweloperskich za pomocą interfejsu API performance.measure(), które są następnie wyświetlane na ścieżce licznika czasu użytkownika w danych śledzenia wydajności, aby pokazać, na czym należy skupić wysiłki w celu zwiększenia wydajności:

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 });

Jeśli ten interfejs API okaże się przydatny w dłuższej perspektywie, prawdopodobnie zostanie włączony do samych Narzędzi deweloperskich, ale poprzedni fragment kodu pozwala na jego wyświetlanie w międzyczasie.

Używanie danych klatek z długich animacji w innych narzędziach dla programistów

Rozszerzenie Web Vitals wykazuje wartość zapisywania danych debugowania podsumowujących w celu diagnozowania problemów z wydajnością. Teraz interfejs API został udostępniony, a takie narzędzia mogą łatwiej wyświetlać dane, aby programiści wiedzieli, na czym powinni skupić swoje wysiłki. Planujemy też dodać tę funkcję do biblioteki JavaScript wskaźników internetowych w wersji 4.

Używanie danych z długich animacji klatek w narzędziach do automatycznego testowania

Narzędzia do automatycznego testowania, na przykład w potokach CI/CD, mogłyby podawać szczegóły potencjalnych problemów z wydajnością przez pomiar długich klatek animacji podczas używania różnych pakietów testowych.

Najczęstsze pytania

Oto niektóre z najczęstszych pytań dotyczących tego interfejsu API:

A może warto po prostu rozszerzyć lub iterować interfejs Long Tasks API?

Jest to alternatywne podejście do raportowania podobnego, lecz w ostatecznym rozrachunku, sposobu mierzenia potencjalnych problemów z responsywnością. Ważne jest, aby witryny używające istniejącego interfejsu Long Tasks API nadal działały, aby nie zakłócać istniejących przypadków użycia.

Chociaż interfejs Long Tasks API może korzystać z niektórych funkcji LoAF (np. lepszego modelu atrybucji), uważamy, że koncentracja na ramkach, a nie na zadaniach, zapewnia wiele korzyści, które sprawiają, że jest to całkowicie inny interfejs API niż istniejący interfejs Long Tasks API.

Czy zastąpi to interfejs Long Tasks API?

Chociaż uważamy, że interfejs Long Animation Frames API jest lepszym i bardziej kompletnym interfejsem API do pomiaru długich zadań, obecnie nie planujemy go wycofać.

Potrzebujemy opinii

Opinię można zgłosić na liście problemów na GitHubie lub błędy w implementacji interfejsu API w Chrome można zgłaszać w narzędziu Chrome do śledzenia problemów.

Podsumowanie

Interfejs Long Animation Frames API to nowy, ekscytujący interfejs API, który ma wiele potencjalnych zalet w porównaniu z poprzednim interfejsem Long Tasks API.

Jest to kluczowe narzędzie do rozwiązywania problemów z responsywnością mierzonych przez INP. INP to trudny wskaźnik do optymalizacji. To jeden ze sposobów, w jaki zespół Chrome chce ułatwić deweloperom identyfikowanie problemów i rozwiązywanie ich.

Zakres interfejsu API Long Animation Frames wykracza jednak poza zakres INP i może pomóc w zidentyfikowaniu innych przyczyn powolnych aktualizacji, które mogą wpływać na ogólną wygodę korzystania z witryny.

Podziękowania

Miniatura: Henry Be, Unsplash.