Interfejs API Long Animation Frames

Interfejs Long Animation Frames API (LoAF, wymawiane jako Lo-Af) to aktualizacja interfejsu Long Tasks API, która ma na celu ułatwienie zrozumienia powolnych aktualizacji interfejsu użytkownika. Może to być przydatne do identyfikowania klatek animacji, które są wolne i mogą wpływać na podstawowy wskaźnik internetowy Interakcja do kolejnego wyrenderowania (INP), który mierzy responsywność, lub innych problemów z płynnością interfejsu, które wpływają na płynność.

Stan interfejsu API

Obsługa przeglądarek

  • Chrome: 123.
  • Edge: 123.
  • Firefox: nieobsługiwane.
  • Safari: nieobsługiwane.

Źródło

Po testowaniu origin w Chrome 116–122 interfejs LoAF API został wprowadzony w Chrome 123.

Wprowadzenie: interfejs Long Tasks API

Obsługa przeglądarek

  • Chrome: 58.
  • Edge: 79.
  • Firefox: nieobsługiwane.
  • Safari: nieobsługiwane.

Źródło

Interfejs API Long Animation Frames jest alternatywą dla interfejsu Long Tasks API, który jest dostępny w Chrome od jakiegoś czasu (od wersji 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 ms. Długie zadania można monitorować za pomocą interfejsu PerformanceLongTaskTiming, korzystając z PeformanceObserver:

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

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

Długotrwałe zadania mogą powodować problemy z szybkością działania. Jeśli użytkownik próbuje wchodzić w interakcję ze stroną, np. kliknąć przycisk lub otworzyć menu, ale główny wątek jest już zajęty długotrwałym zadaniem, interakcja użytkownika jest opóźniona, ponieważ czeka na zakończenie tego zadania.

Aby zwiększyć szybkość reakcji, często zaleca się podzielenie długich zadań. Jeśli każde długie zadanie zostanie podzielone na serię krótszych zadań, możesz między nimi wykonywać ważniejsze zadania, aby uniknąć znacznych opóźnień w reagowaniu na interakcje.

Dlatego, gdy chcesz poprawić szybkość reakcji, pierwszym krokiem jest często uruchomienie śledzenia wydajności i sprawdzenie długich zadań. Możesz to zrobić za pomocą narzędzia do testów laboratoryjnych, takiego jak Lighthouse (które zawiera kontrolę Unikaj długich zadań w wątku głównym) lub sprawdzając długie zadania w Chrome DevTools.

Testowanie w laboratorium często nie jest dobrym punktem wyjścia do identyfikowania problemów z responsywnością, ponieważ te narzędzia mogą nie uwzględniać interakcji – a jeśli tak, to tylko niewielkiej ich podzbioru. Najlepiej jest mierzyć przyczyny powołujące do wydłużenia się interakcji w polu.

Wady interfejsu Long Tasks API

Pomiar długich zadań w polu za pomocą narzędzia Performance Observer jest tylko w pewnym stopniu przydatny. W rzeczywistości nie zawiera ono zbyt wielu informacji, poza tym, że miało miejsce długie zadanie i ile ono trwało.

Narzędzia do monitorowania rzeczywistych użytkowników (RUM) często wykorzystują te dane do analizy trendów dotyczących liczby lub czasu trwania długich zadań oraz do określania, na których stronach występują. Jednak bez szczegółowych informacji o przyczynach długiego czasu wykonywania zadań ich przydatność jest ograniczona. Interfejs Long Tasks API ma tylko podstawowy model atrybucji, który w najlepszym przypadku podaje tylko kontener, w którym wystąpiło długie zadanie (dokument najwyższego poziomu lub <iframe>), ale nie skryptu ani funkcji, które je wywołały. Przykładowy wpis:

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

Interfejs API Long Tasks również nie jest pełnym widokiem, ponieważ może pomijać niektóre ważne zadania. Niektóre aktualizacje, np. renderowanie, odbywają się w osobnych zadaniach, które najlepiej jest uwzględnić razem z poprzednim wykonaniem, które spowodowało tę aktualizację, aby dokładnie mierzyć „całkowity wysiłek” związany z tą interakcją. Więcej informacji o ograniczeniach związanych z wykonywaniem zadań znajdziesz w tej części poradnika, w której omawiamy wady długich zadań.

Ostatnim problemem jest to, że pomiar długich zadań obejmuje tylko poszczególne zadania, które trwają dłużej niż 50 ms. Klatka animacji może składać się z kilku zadań mniejszych niż ten limit 50 ms, ale w sumie nadal blokować renderowanie przez przeglądarkę.

Interfejs Long Animation Frames API

Obsługa przeglądarek

  • Chrome: 123.
  • Edge: 123.
  • Firefox: nieobsługiwane.
  • Safari: nieobsługiwane.

Źródło

Long Animation Frames API (LoAF) to nowy interfejs API, który ma na celu wyeliminowanie niektórych niedoskonałości interfejsu Long Tasks API, aby umożliwić deweloperom uzyskiwanie bardziej przydatnych informacji, które pomogą im rozwiązać problemy z szybkością działania i poprawią INP, a także uzyskać informacje o płynności.

Dobra responsywność oznacza, że strona szybko reaguje na interakcje z użytkownikiem. Oznacza to, że system musi być w stanie na czas malować wszystkie aktualizacje potrzebne użytkownikowi, a także nie może blokować tych aktualizacji. W przypadku INP zalecamy, aby czas reakcji nie przekraczał 200 milisekund, ale w przypadku innych aktualizacji (np. animacji) nawet 200 milisekund może być zbyt długim czasem.

Interfejs API Long Animation Frames to alternatywny sposób pomiaru blokowania pracy. Jak sama nazwa wskazuje, interfejs Long Animation Frames API mierzy nie poszczególne zadania, ale długie klatki animacji. Długi element animacji to taki, którego aktualizacja renderowania jest opóźniona o więcej niż 50 ms (wartość ta jest taka sama jak próg dla interfejsu Long Tasks API).

Długie klatki animacji są mierzone od początku zadań, które wymagają renderowania. Jeśli pierwsze zadanie w potencjalnej długiej klatce animacji nie wymaga renderowania, długa klatka animacji kończy się po zakończeniu zadania niewymagającego renderowania, a nowa potencjalna długa klatka animacji rozpoczyna się wraz z kolejną czynnością. Takie nierenderowane długie klatki animacji są nadal uwzględniane w Long Animation Frames API, jeśli ich długość przekracza 50 ms (a czas renderStart wynosi 0), aby umożliwić pomiar potencjalnie blokującej pracy.

Długie klatki animacji można obserwować podobnie jak długie zadania z wartością PerformanceObserver, ale z uwzględnieniem typu long-animation-frame:

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

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

Poprzednie długie klatki animacji można też wyszukiwać na osi czasu skuteczności w ten sposób:

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

Jest jednak wartość maxBufferSize dla wpisów dotyczących skuteczności, po której nowsze wpisy są pomijane, dlatego zalecamy podejście PerformanceObserver. Rozmiar bufora long-animation-frame jest ustawiony na 200, tak samo jak w przypadku long-tasks.

Zalety analizowania ramek zamiast zadań

Główną zaletą analizowania tego zagadnienia z poziomu klatek, a nie zadań, jest to, że długa animacja może składać się z dowolnej liczby zadań, które łącznie tworzą długą klatkę animacji. Rozwiązaniem jest ostatni wspomniany wcześniej problem, w którym suma wielu mniejszych zadań blokujących renderowanie przed ramką animacji może nie być wyświetlana przez interfejs API Long Tasks.

Kolejną zaletą tego alternatywnego widoku długich zadań jest możliwość wyświetlania podziału czasu w całym interwale. W odróżnieniu od interfejsu Long Tasks API, który zawiera tylko startTimeduration, interfejs Long Tasks for Frames zawiera znacznie bardziej szczegółowy podział na różne części czasu trwania ramki.

Sygnatury czasowe i czas trwania klatek

  • startTime: czas rozpoczęcia długiej klatki animacji w stosunku do czasu rozpoczęcia nawigacji.
  • duration: czas trwania długiej klatki animacji (nie uwzględnia czasu prezentacji).
  • renderStart: czas rozpoczęcia cyklu renderowania, który obejmuje wywołania requestAnimationFrame, obliczenia stylu i układu, wywołania obserwatora zmiany rozmiaru i wywołania obserwatora przecięcia.
  • styleAndLayoutStart: początek okresu spędzonego na obliczaniu stylu i układu.
  • firstUIEventTimestamp: czas pierwszego zdarzenia interfejsu (mysz/klawiatura itp.) do obsłużenia w ramach tego interwału.
  • blockingDuration: łączny czas trwania w milisekundach, przez który ramka animacji blokuje przetwarzanie danych wejściowych lub innych zadań o wysokim priorytecie.

wyjaśnienie terminu blockingDuration,

Długa klatka animacji może składać się z wielu zadań. Wartość blockingDuration to suma czasów trwania zadań dłuższych niż 50 ms (w tym czas trwania renderowania w najdłuższym zadaniu).

Jeśli na przykład długi kadr animacji składał się z 2 zadań trwających odpowiednio 55 i 65 ms, a następnie z renderowania trwającego 20 ms, to duration będzie wynosić około 140 ms, a blockingDuration (55 - 50) + (65 + 20 - 50) = 40 ms. W ciągu 40 milisekund z 140 milisekund trwania tego klatki animacji uznaliśmy, że blokuje ona przetwarzanie danych wejściowych.

Czy chcesz sprawdzić duration czy blockingDuration

W przypadku wyświetlacza o częstotliwości 60 Hz przeglądarka będzie próbować zaplanować wyświetlenie klatki co najmniej co 16, 66 ms (aby zapewnić płynne aktualizacje) lub po wykonaniu zadania o wysokiej priorytecie, np.obsługi wprowadzania danych (aby zapewnić szybkie aktualizacje). Jeśli jednak nie ma danych wejściowych ani innych zadań o wysokim priorytecie, ale jest kolejka innych zadań, przeglądarka zwykle kontynuuje bieżącą ramkę przez ponad 16,66 ms niezależnie od tego, jak dobrze są w niej rozmieszczone zadania. Oznacza to, że przeglądarka zawsze będzie starała się nadawać priorytety danym wejściowym, ale może zdecydować się na przetworzenie kolejki zadań zamiast aktualizacji. Wynika to z tego, że renderowanie jest kosztownym procesem, więc przetworzenie połączonego zadania renderowania dla wielu zadań zwykle prowadzi do ogólnego zmniejszenia pracy.

Dlatego długie klatki animacji z niskim lub zerowym blockingDuration powinny nadal reagować na dane wejściowe. Zmniejszenie lub wyeliminowanie blockingDuration przez podzielenie długich zadań jest więc kluczem do poprawy szybkości reakcji mierzonej za pomocą INP.

Jednak duża liczba długich klatek animacji, niezależnie od wartości blockingDuration, wskazuje na opóźnienia w aktualizacjach interfejsu, które mogą wpływać na płynność i spowodować wrażenie opóźnień w przewijaniu lub animacjach, nawet jeśli nie stanowią one większego problemu w zakresie szybkości reakcji mierzonej za pomocą INP. Aby zrozumieć problemy w tej dziedzinie, sprawdź duration. Optymalizacja pod kątem tych problemów może być trudniejsza, ponieważ nie można ich rozwiązać przez podzielenie pracy, ale trzeba zamiast tego zmniejszyć jej ilość.

Czasy klatek

Wspomniane wcześniej sygnatury czasowe umożliwiają podzielenie długiej klatki animacji na etapy:

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 przed układem styleAndLayoutStart ? styleAndLayoutStart - renderStart : 0
Renderowanie: czas trwania stylizacji i układu styleAndLayoutStart ? (startTime + duration) - styleAndLayoutStart : 0

Lepsza atrybucja skryptu

Typ wpisu long-animation-frame zawiera lepsze dane atrybucji każdego skryptu, który przyczynił się do utworzenia długiego kadru animacji (w przypadku skryptów dłuższych niż 5 ms).

Podobnie jak w przypadku interfejsu Long Tasks API, dane te będą udostępniane w tablicy pozycji atrybucji, z których każda zawiera:

  • Obie funkcje nameEntryType zwracają wartość script.
  • znaczący parametr invoker, który wskazuje, jak wywołano skrypt (np. 'IMG#id.onload', 'Window.requestAnimationFrame' lub 'Response.json.then');
  • invokerType punktu wejścia skryptu:
    • user-callback: znany wywołanie zwrotne zarejestrowane w interfejsie API platformy internetowej (na przykład setTimeout, requestAnimationFrame).
    • event-listener: listener (nasłuchujący) zdarzenia platformy (np. click, load, keyup).
    • resolve-promise: obsługa platformy (na przykład fetch(). Pamiętaj, że w przypadku obietnic wszystkie ich przetwarzacze są mieszane razem w jeden „skrypt”.
    • reject-promise: tak jak w resolve-promise, ale z odrzuceniem.
    • classic-script: ocena skryptu (np. <script> lub import())
    • module-script: to samo co classic-script, ale w przypadku skryptów modułów.
  • oddzielne dane o czasowaniu dla tego skryptu:
    • startTime: czas wywołania funkcji wpisu.
    • duration: czas od startTime do zakończenia przetwarzania kolejnej kolejki mikrozadań.
    • executionStart: czas po skompilowaniu.
    • forcedStyleAndLayoutDuration: łączny czas poświęcony na przetworzenie wymuszonej składni i stylu w ramach tej funkcji (patrz thrashing).
    • pauseDuration: łączny czas spędzony na „wstrzymywaniu” operacji synchronicznych (alert, synchroniczne żądania XHR).
  • Szczegóły źródła skryptu:
    • sourceURL: nazwa zasobu skryptu, jeśli jest dostępna (lub pusta, jeśli nie można jej znaleźć).
    • 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 można jej znaleźć).
  • windowAttribution: kontener (dokument najwyższego poziomu lub <iframe>), w którym wystąpiła długa animacja.
  • window: odwołanie do okna o tym samym pochodzeniu.

W przypadku, gdy są dostępne, wpisy źródłowe pozwalają deweloperom dokładnie określić, jak został wywołany każdy skrypt w ramach długiej animacji, aż do pozycji znaku w skrypcie wywołującym. Dzięki temu możesz określić dokładną lokalizację w zasobie JavaScriptu, która spowodowała długi kadr animacji.

Przykład wpisu dotyczącego skuteczności long-animation-frame

Przykład pełnego wpisu oceny long-animation-frame zawierającego 1 skrypt:

{
  "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ć, daje to niespotykaną ilość danych, które umożliwiają ustalenie przyczyny opóźnień w renderowaniu.

Korzystanie z interfejsu Long Animation Frames API w praktyce

Narzędzia takie jak Narzędzia deweloperskie w Chrome i Lighthouse, choć przydatne do wykrywania i odtwarzania problemów, są narzędziami laboratoryjnymi, które mogą pomijać ważne aspekty wrażeń użytkownika, które są dostępne tylko w danych z pola.

Interfejs API Long Animation Frames został zaprojektowany do użytku w polu, aby zbierać ważne dane kontekstowe dotyczące interakcji użytkowników, których nie można było zebrać za pomocą interfejsu Long Tasks API. Pomoże Ci to wykryć i odtworzyć problemy z interaktywnością, które w innym przypadku mogłyby pozostać niezauważone.

Obsługa interfejsu API wykrywania ramek długich animacji

Aby sprawdzić, czy interfejs API jest obsługiwany, możesz użyć tego kodu:

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

Najbardziej oczywistym zastosowaniem interfejsu API Long Animation Frames jest diagnostyka i rozwiązywanie problemów z czasem od interakcji do następnego wyświetlenia (INP). Był to jeden z głównych powodów, dla których zespół Chrome opracował ten interfejs API. Dobry INP to taki, w którym wszystkie interakcje są obsługiwane w ciągu 200 ms lub krócej od momentu interakcji do momentu wyświetlenia ramki. Ponieważ interfejs API Long Animation Frames mierzy wszystkie ramki, które zajmują 50 ms lub dłużej, większość problematycznych INP powinna zawierać dane LoAF, które pomogą w diagnozowaniu tych interakcji.

„LoAF INP” to LoAF, który obejmuje interakcję INP, jak pokazano na diagramie poniżej:

Przykłady długich klatek animacji na stronie z wyróżnionym INP LoAF
Strona może mieć wiele LoAF, z których jeden jest powiązany z interakcją INP.

W niektórych przypadkach zdarzenie INP może obejmować 2 ramki LoAF – zwykle wtedy, gdy interakcja następuje po rozpoczęciu przez ramkę renderowania części poprzedniej ramki. W takim przypadku przetwarzanie elementu obsługującego zdarzenie odbywa się w ramce następnej:

Przykłady długich klatek animacji na stronie z wyróżnionym INP LoAF
Strona może mieć wiele LoAF, z których jeden jest powiązany z interakcją INP.

W niektórych rzadkich przypadkach może ona obejmować więcej niż 2 LoAF.

Rejestrowanie danych LoAF związanych z interakcją z użyciem INP pozwala uzyskać więcej informacji o tej interakcji, co ułatwi jej diagnostykę. Jest to szczególnie przydatne, gdy chcesz poznać opóźnienie wprowadzania danych: możesz zobaczyć, jakie inne skrypty były wykonywane w danym ujęciu.

Może to być też przydatne, aby zrozumieć niewyjaśnione czasy przetwarzaniaopóźnienia wyświetlania, jeśli Twoje metody obsługi zdarzeń nie odtwarzają wartości, które są widoczne w przypadku innych skryptów, które mogą być uruchamiane dla użytkowników, ale nie są uwzględniane w Twoich testach.

Nie ma bezpośredniego interfejsu API do łączenia wpisu INP z powiązanymi wpisami LoAF, ale można to zrobić w kodzie, porównując czasy rozpoczęcia i zakończenia każdego z nich (patrz przykładowy skrypt WhyNp). Biblioteka web-vitals obejmuje wszystkie nakładające się punkty interakcji użytkownika w usłudze longAnimationFramesEntries w interfejsie przypisywania INP w wersji 4.

Po połączeniu wpisu lub wpisów z LoAF możesz dodać informacje z atribucją INP. Obiekt scripts zawiera jedne z najcenniejszych informacji, ponieważ może pokazywać, co jeszcze było wykonywane w tych ramach. Przesyłanie tych danych do usługi analitycznej pozwoli Ci lepiej zrozumieć, dlaczego interakcje były powolne.

Raportowanie LoAF w przypadku interakcji INP to dobry sposób na znalezienie najbardziej palących problemów z interaktywnością na stronie. Każdy użytkownik może inaczej wchodzić w interakcję ze stroną, a przy wystarczającej ilości danych o przypisaniu INP w tych danych może się pojawić pewna liczba potencjalnych problemów. Pozwala to na sortowanie skryptów według liczby, aby sprawdzić, które skrypty są powiązane z powolnym INP.

Przesyłanie do punktu końcowego analityki więcej danych o długich animacjach

Jednym z minusów analizowania tylko raportów o nieprawidłowościach w usłudze INP jest to, że możesz przeoczyć inne potencjalne obszary do ulepszenia, które mogą spowodować przyszłe problemy z INP. Może to wywołać uczucie ścigania własnego ogona, gdy naprawisz problem z czasem bezczynności, oczekując znacznej poprawy, a następna najdłuższa interakcja jest tylko nieznacznie lepsza, więc czas bezczynności nie ulega znacznemu skróceniu.

Zamiast analizować tylko LoAF w przypadku INP, warto wziąć pod uwagę wszystkie LoAF w całym okresie istnienia strony:

Strona z wiele LoAF, z których niektóre występują podczas interakcji, nawet jeśli nie jest to interakcja INP.
Przeglądanie wszystkich LoAF może pomóc w identyfikowaniu przyszłych problemów z INP.

Każdy wpis w LoAF zawiera jednak sporo danych, więc prawdopodobnie zechcesz ograniczyć analizę tylko do niektórych LoAF. Dodatkowo, ponieważ wpisy z długimi klatkami animacji mogą być dość duże, deweloperzy powinni zdecydować, które dane z tego wpisu powinny być wysyłane do usługi Analytics. Na przykład podsumowanie czasu trwania wpisu i być może nazwy skryptów lub inny minimalny zestaw innych danych kontekstowych, które mogą być potrzebne.

Oto kilka sugerowanych wzorów, które pomogą Ci zmniejszyć ilość danych dotyczących długich klatek animacji:

Który z tych wzorów sprawdzi się najlepiej, zależy od tego, jak daleko zaawansowałeś/zaawansowałaś w optymalizacji i jak często występują długie klatki animacji. W przypadku witryny, która nigdy wcześniej nie była optymalizowana pod kątem responsywności, może być wiele elementów LoAF. Możesz ograniczyć się do elementów LoAF z interakcjami, ustawić wysoki próg lub analizować tylko najgorsze elementy.

Po rozwiązaniu typowych problemów z czasem reakcji możesz rozszerzyć te ustawienia, nie ograniczając się tylko do interakcji lub długich blokad ani nie obniżając progów.

Obserwowanie długich klatek animacji z interakcjami

Aby uzyskać więcej informacji niż tylko długie ramki animacji INP, możesz sprawdzić wszystkie ramki LoAF z interakcjami (które można wykryć na podstawie obecności wartości firstUIEventTimestamp) o wysokiej wartości blockingDuration.

Może to być też łatwiejsza metoda monitorowania LoAF w ujęciu INP niż próba znalezienia korelacji między tymi dwoma wartościami, która może być bardziej skomplikowana. W większości przypadków obejmuje to czas trwania interakcji INP w przypadku danej wizyty, a w rzadkich przypadkach, gdy tak się nie dzieje, nadal wyświetla długie interakcje, które należy poprawić, ponieważ mogą być interakcjami INP w przypadku innych użytkowników.

Poniższy kod rejestruje wszystkie wpisy LoAF z wartością blockingDuration większą niż 100 ms, w przypadku których interakcja miała miejsce w ramach danej klatki. Wartość 100 została wybrana, ponieważ jest niższa niż „dobry” próg INP wynoszący 200 milisekund. Możesz wybrać wyższą lub niższą wartość w zależności od potrzeb.

const REPORTING_THRESHOLD_MS = 100;

const observer = new PerformanceObserver(list => {
  for (const entry of list.getEntries()) {
    if (entry.blockingDuration > 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 });

Obserwowanie długich klatek animacji z długim czasem blokowania

Zamiast sprawdzać wszystkie długie klatki animacji z interakcjami, możesz przyjrzeć się wszystkim długim klatkom animacji z długim czasem blokowania. Wskazują one na potencjalne problemy z INP, jeśli użytkownik wejdzie w interakcję w długich klatkach animacji.

Poniższy kod rejestruje wszystkie wpisy LoAF z czasem blokowania dłuższym niż 100 ms, w przypadku których nastąpiła interakcja w ramach danej klatki. Wartość 100 wybrano, ponieważ jest ona mniejsza niż „dobry” próg INP wynoszący 200 ms, co pomaga zidentyfikować potencjalne klatki problemowe, a jednocześnie ogranicza do minimum liczbę zgłaszanych długich klatek animacji. Możesz wybrać wyższą lub niższą wartość w zależności od potrzeb.

const REPORTING_THRESHOLD_MS = 100;

const observer = new PerformanceObserver(list => {
  for (const entry of list.getEntries()) {
    if (entry.blockingDuration > 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 });

Obserwowanie długich klatek animacji podczas krytycznych aktualizacji interfejsu użytkownika w celu poprawy płynności

Jak już wspomnieliśmy, analiza ramek animacji o długim czasie blokowania może pomóc w zwiększeniu szybkości reakcji na dane wejściowe. Jednak aby sprawdzić płynność, należy przyjrzeć się wszystkim długim klatkom animacji z długim duration.

Ponieważ może to powodować zakłócenia, możesz ograniczyć pomiary tych punktów do kluczowych punktów w takim schemacie:

const REPORTING_THRESHOLD_MS = 100;

const observer = new PerformanceObserver(list => {
  if (measureImportantUIupdate) {
    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 });

async function doUIUpdatesWithMeasurements() {
  measureImportantUIupdate = true;
  await doUIUpdates();
  measureImportantUIupdate = false;
}

Obserwowanie najgorszych długich klatek animacji

Zamiast ustawiać określony próg, witryny mogą zbierać dane o najdłuższym klatce animacji (lub klatkach), aby zmniejszyć ilość danych, które należy beaconować. Niezależnie od tego, ile klatek animacji jest wyświetlanych na stronie, z serwera beacona zwracane są tylko dane o najgorszych 5, 10 lub innej liczbie klatek animacji, która jest absolutnie niezbędna.

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

Te strategie można też łączyć – wystarczy przyjrzeć się 10 najgorszym LoAF z interakcjami dłuższymi niż 100 ms.

W odpowiednim momencie (najlepiej w zdarzeniu visibilitychange) beacon wraca do Analytics. Do testów lokalnych możesz okresowo używać console.table:

console.table(longestBlockingLoAFs);

Wykrywanie typowych wzorców w długich klatkach animacji

Inną strategią może być sprawdzenie najpopularniejszych skryptów w długich wpisach dotyczących ram animacji. Dane mogą być raportowane na poziomie skryptu i pozycji znaku, aby umożliwić identyfikację osób, które popełniają takie naruszenia wielokrotnie.

Ta metoda może się szczególnie dobrze sprawdzać w przypadku platform z możliwością dostosowania, na których można zidentyfikować motywy lub wtyczki powodujące problemy z wydajnością w wielu witrynach.

Czas wykonywania typowych skryptów lub skryptów pochodzących od innych firm w długich klatkach animacji można zsumować i przekazać w raportach, aby zidentyfikować elementy, które przyczyniają się do długich klatek animacji w całej witrynie lub w kolekcji 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ład takich danych wyjściowych:

(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

Korzystanie z interfejsu Long Animation Frames API w narzędziach

Interfejs API umożliwia też korzystanie z dodatkowych narzędzi dla deweloperów do debugowania lokalnego. Chociaż niektóre narzędzia, takie jak Lighthouse i Narzędzie deweloperskie w Chrome, były w stanie zebrać wiele z tych danych za pomocą szczegółów śledzenia na niższym poziomie, interfejs API na wyższym poziomie może umożliwić dostęp do tych danych innym narzędziom.

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

Za pomocą interfejsu API performance.measure() możesz wyświetlać w narzędziach DevTools długie klatki animacji, które są następnie widoczne w śladzie Czasy użytkownika w narzędziach DevTools w śladach wydajności. Pozwala to określić, na czym należy się skupić, aby poprawić wydajność. Za pomocą interfejsu DevTools Extensibility API można nawet wyświetlać je na osobnej ścieżce:

const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    performance.measure('LoAF', {
      start: entry.startTime,
      end: entry.startTime + entry.duration,
      detail: {
        devtools: {
          dataType: "track-entry",
          track: "Long animation frames",
          trackGroup: "Performance Timeline",
          color: "tertiary-dark",
          tooltipText: 'LoAF'
        }
      }
    });
  }
});

observer.observe({ type: 'long-animation-frame', buffered: true });
Ślad w panelu wydajności DevTools z niestandardową ścieżką zawierającą dane o długiej klatce animacji, które można porównać z główną wykresem słupkowym.
Wyświetlanie w Narzędziach deweloperskich danych dotyczących długiego klatki animacji.

W dłuższej perspektywie długie klatki animacji zostaną prawdopodobnie wbudowane w Narzędzia deweloperskie, ale do tego czasu można wyświetlać je za pomocą poprzedniego fragmentu kodu.

Pierwszy wpis na poprzednim rysunku pokazuje też, gdzie przeglądarka przetworzyła kilka zadań w jednej długiej klatce animacji, zamiast renderować je osobno. Jak już wspomnieliśmy, może się tak zdarzyć, gdy nie ma zadań wejściowych o wysokim priorytecie, ale jest kolejka zadań. Pierwsze długie zadanie wymaga wykonania pewnych aktualizacji renderowania (w przeciwnym razie bieżąca długa klatka animacji zostanie zresetowana, a nowa rozpocznie się wraz z kolejnym zadaniem), ale zamiast natychmiast wykonać renderowanie, przeglądarka przetworzyła kilka dodatkowych zadań, a dopiero potem wykonała długie zadanie renderowania i zakończyła długą klatkę animacji. Pokazuje to, że warto sprawdzać długie klatki animacji w DevTools, a nie tylko długie zadania, aby wykrywać opóźnione renderowanie.

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

Rozszerzenie Web Vitals wyświetla wartość w podsumowaniu logowania w informacjach debugowania, aby diagnozować problemy z wydajnością.

Dane te obejmują teraz również dane o długich klatkach animacji w przypadku każdego wywołania zwrotnego INP i każdej interakcji:

Logowanie w konsoli rozszerzenia Web Vitals.
Logowanie na konsoli rozszerzenia Core Web Vitals wykorzystuje dane LoAF.

Korzystanie z danych dotyczących długich klatek animacji w narzędziach testowania automatycznego

Podobnie narzędzia do automatycznego testowania w przepływach CI/CD mogą wyświetlać szczegóły dotyczące potencjalnych problemów ze skutecznością, mierząc długie klatki animacji podczas uruchamiania różnych zestawów testów.

Najczęstsze pytania

Oto kilka najczęstszych pytań dotyczących tego interfejsu API:

Dlaczego nie rozszerzyć interfejsu Long Tasks API lub nie użyć go wielokrotnie?

To alternatywny sposób raportowania podobnych, ale różniących się pomiarów potencjalnych problemów z szybkością działania. Ważne jest, aby witryny korzystające z obecnego interfejsu Long Tasks API nadal działały, aby nie zakłócać dotychczasowych zastosowań.

Chociaż interfejs Long Tasks API może korzystać z niektórych funkcji LoAF (np. z lepszego modelu atrybucji), uważamy, że skupienie się na klatkach zamiast na zadaniach przynosi wiele korzyści, które sprawiają, że ten interfejs API różni się od dotychczasowego Long Tasks API.

Dlaczego nie mam wpisów skryptu?

Może to oznaczać, że długi kadr animacji nie był spowodowany przez Javę, ale przez duże obciążenie renderowania.

Może się tak też zdarzyć, gdy długi kadr animacji jest spowodowany przez JavaScript, ale nie można przypisać mu atrybucji ze względu na różne powody związane z prywatnością, o których była już mowa (głównie dlatego, że JavaScript nie należy do strony).

Dlaczego mam wpisy skryptu, ale nie mam informacji o źródle lub są one ograniczone?

Może się tak zdarzyć z wielu powodów, m.in. dlatego, że nie ma odpowiedniego źródła.

Informacje o skrypcie będą ograniczone tylko do sourceURL (z wyłączeniem przekierowań) w przypadku skryptów no-cors cross-origin, pustego ciągu znaków w przypadku skryptu sourceFunctionName i wartości -1 w przypadku skryptu sourceCharPosition. Można to rozwiązać, pobierając te skrypty za pomocą CORS, dodając crossOrigin = "anonymous" do wywołania <script>.

Na przykład domyślny skrypt Menedżera tagów Google, który należy dodać do strony:

<!-- Google Tag Manager -->
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-XXXXXXX');</script>
<!-- End Google Tag Manager -->

Można go rozszerzyć o element j.crossOrigin = "anonymous", aby umożliwić udostępnianie pełnych szczegółów atrybucji w Google Tag Manager.

Czy Long Tasks API zostanie zastąpione?

Uważamy, że interfejs Long Animation Frames API jest lepszym, bardziej kompletnym interfejsem do pomiaru długich zadań, ale obecnie nie planujemy wycofać interfejsu Long Tasks API.

Prośba o opinię

Opinie możesz przesyłać na liście problemów na GitHubie, a błędy w implementacji interfejsu API w Chrome możesz zgłaszać w śledzeniu problemów w Chrome.

Podsumowanie

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

Okazuje się, że jest to kluczowe narzędzie do rozwiązywania problemów z szybkością reakcji, które są mierzone za pomocą INP. INP to dane, które trudno jest zoptymalizować, a ten interfejs API to jeden ze sposobów, w jaki zespół Chrome ułatwia programistom identyfikowanie i rozwiązywanie problemów.

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

Podziękowania

Miniatura autorstwa Henry Be z Unsplash.