Jak i dlaczego opracowaliśmy statystyki skuteczności

W Chrome 102 w Narzędziach deweloperskich znajdziesz nowy eksperymentalny panel Statystyki wydajności. W tym poście porozmawiamy nie tylko o powodach pracy nad nowym panelem, ale także o wyzwaniach technicznych, które się stają, i decyzjach, które podjęliśmy.

ALT_TEXT_HERE

Po co tworzyć kolejny panel?

(jeśli jeszcze go nie widzisz, obejrzyj film, w którym wyjaśniamy, dlaczego warto tworzyć panel Statystyki skuteczności i jak z jego pomocą można uzyskiwać przydatne informacje o skuteczności witryny).

Istniejący panel Skuteczność to świetne narzędzie, jeśli chcesz mieć wszystkie dane o swojej witrynie w jednym miejscu, ale uznaliśmy, że może on być nieco przytłaczający. Jeśli nie jesteś ekspertem od występów, ciężko wiedzieć, na co zwrócić uwagę i które fragmenty nagrania są istotne.

Otwórz panel statystyk, w którym nadal możesz wyświetlić osi czasu przechwycenia i przeanalizować dane, a także uzyskać przydatną listę tego, co narzędzia DevTools uznają za główne „statystyki”, które warto poznać. Statystyki wykrywają problemy, takie jak żądania blokujące renderowanie, zmiany układu czy długie zadania, które mogą negatywnie wpływać na wydajność wczytywania stron w witrynie, a w szczególności na wyniki podstawowych wskaźników internetowych. Oprócz oznaczenia problemów Statystyki wydajności będą zawierać przydatne sugestie dotyczące zwiększania wyników CWV oraz linki do dodatkowych zasobów i dokumentacji.

Link do opinii w panelu

Ten panel jest eksperymentalny, dlatego chętnie poznamy Twoją opinię. Daj nam znać, jeśli napotkasz jakieś błędy lub masz propozycje funkcji, które Twoim zdaniem pomogą Ci w poprawie wydajności witryny.

Jak tworzyliśmy Statystyki wydajności

Podobnie jak w przypadku innych narzędzi DevTools, funkcja Performance Insights została napisana w języku TypeScript, a do tworzenia interfejsu użytkownika wykorzystaliśmy komponenty internetowe oparte na lit-html. Różnica polega na tym, że główny interfejs użytkownika to element HTML canvas, a oś czasu jest narysowana na tym płótnie. Wiele złożoności wynika z zarządzania tym obszarem roboczym: nie tylko rysowania odpowiednich szczegółów we właściwym miejscu, ale też zarządzania zdarzeniami myszy (np. gdzie użytkownik kliknął na obszarze roboczym? Czy kliknęli zdarzenie, które narysowaliśmy?) i sprawdź, czy skutecznie ponownie renderujemy płótno.

Wiele ścieżek na 1 obszarze roboczym

W przypadku danej witryny jest wiele „ścieżek”, które chcemy renderować, a każda z nich reprezentuje inną kategorię danych. Na przykład panel Statystyki domyślnie wyświetla 3 ścieżki:

W miarę dodawania kolejnych funkcji do panelu spodziewamy się, że będzie on zawierał więcej ścieżek.

Na początku zastanawialiśmy się, czy w przypadku każdej z tych ścieżek utworzyć własny <canvas>, dzięki czemu widok główny zamieni się w kilka elementów canvas ułożonych w pionie. Uprościłoby to renderowanie na poziomie ścieżki, ponieważ każda ścieżka mogłaby być renderowana oddzielnie, a nie byłoby zagrożenia, że ścieżka zostanie wyrenderowana poza swoimi granicami. Niestety takie podejście ma 2 główne problemy:

Elementy canvas są kosztowne w (ponownym) renderowaniu; korzystanie z wielu kanw jest droższe niż korzystanie z jednej, nawet jeśli ta ostatnia jest większa. Renderowanie nakładek, które obejmują wiele ścieżek (np. pionowe linie do oznaczania zdarzeń, takich jak czas FCP), staje się skomplikowane: musimy renderować na wielu polach i zadbać o to, aby wszystkie były renderowane razem i odpowiednio wyrównane.

Korzystanie z jednego canvas w całym interfejsie wymagało od nas znalezienia sposobu na to, aby każda ścieżka renderowana była w odpowiednich współrzędnych i nie zachodziła na inną ścieżkę. Jeśli na przykład dana ścieżka ma wysokość 100 pikseli, nie możemy pozwolić, aby renderowanie czegoś, co ma wysokość 120 pikseli, powodowało przenikanie obrazu na ścieżkę znajdującą się poniżej. Aby rozwiązać ten problem, możemy użyć clip. Przed wyrenderowaniem każdej ścieżki rysujemy prostokąt reprezentujący widoczne okno ścieżki. Dzięki temu wszystkie ścieżki narysowane poza tymi granicami zostaną przycięte przez kanwę.

canvasContext.beginPath();
canvasContext.rect(
    trackVisibleWindow.x, trackVisibleWindow.y, trackVisibleWindow.width, trackVisibleWindow.height);
canvasContext.clip();

Nie chcieliśmy też, aby każda ścieżka wiedziała, gdzie znajduje się w pionie: każda ścieżka powinna renderować się tak, jakby renderowała się w punkcie (0, 0). Mamy też komponent wyższego poziomu (który nazywamy TrackManager), który zarządza ogólną pozycją ścieżki. Można to zrobić za pomocą parametru translate, który przesuwa kanwę o podane współrzędne (x, y). Na przykład:

canvasContext.translate(0, 10); // Translate by 10px in the y direction
canvasContext.rect(0, 0, 10, 10); // draw a rectangle at (0, 0) that’s 10px high and wide

Pomimo ustawienia kodu rect jako pozycji 0, 0, ogólne przesunięcie spowoduje, że prostokąt zostanie wyrenderowany w pozycji 0, 10. Dzięki temu możemy pracować na podstawie ścieżki, tak jakby renderowanie było ustawione na (0, 0), i zlecać naszemu menedżerowi ścieżki tłumaczenie każdej ścieżki w celu zapewnienia prawidłowego renderowania poniżej poprzedniej.

Płótna poza ekranem dla ścieżek i fragmentów

Renderowanie obszaru roboczego jest stosunkowo drogie, dlatego chcemy mieć pewność, że panel Trendy będzie działał płynnie i elastycznie. Czasami nie można uniknąć ponownego renderowania całej przestrzeni roboczej – na przykład jeśli zmienisz poziom powiększenia, trzeba będzie zacząć od nowa i wyrenderować wszystko od nowa. Ponownie renderowanie obrazu jest szczególnie kosztowne, ponieważ nie można renderować tylko jego części. Trzeba wyczyścić całą powierzchnię obrazu i na nowo go narysować. Jest to inne podejście niż ponowne renderowanie DOM, w którym narzędzia mogą obliczyć minimalną ilość wymaganej pracy i nie usuwać wszystkiego, aby zacząć od nowa.

Jednym z obszarów, w których wystąpiły problemy z obrazem, było wyróżnianie. Gdy najedziesz kursorem na dane w panelu, zostaną one wyróżnione na osi czasu. Podobnie, gdy najedziesz kursorem na statystyki dotyczące danego zdarzenia, zostanie ono otoczone niebieską obwódką.

Ta funkcja została zaimplementowana w taki sposób, że wykrywała ruch myszy nad elementem, który uruchamia podświetlenie, a potem rysowała to podświetlenie bezpośrednio na głównym płótnie. Problem pojawia się, gdy trzeba usunąć podświetlenie. Jedyną opcją jest przekreślenie wszystkich elementów. Nie da się ponownie narysować obszaru, w którym było najciekawsze (nie było to bez znaczących zmian architektonicznych), ale ponownie narysowaliśmy cały obszar roboczy, ponieważ chcemy usunąć niebieskie obramowanie wokół jednego elementu, który wydaje się przerysowany. Może też występować opóźnienie wizualne, jeśli szybko przesuniesz kursor myszy nad różne elementy, aby wywołać wiele wyróżnień w krótkich odstępach czasowych.

Aby to naprawić, podzieliliśmy nasz interfejs na 2 obszary poza ekranem: „główną” przestrzeń roboczą, w której są renderowane ścieżki, oraz obszar roboczy z najciekawszymi momentami, gdzie rysowane są podświetlenia. Następnie renderujemy te obszary robocze, kopiując je do pojedynczego obszaru roboczego widocznego na ekranie użytkownika. Metodę drawImage można zastosować w kontekście kanwy, która może przyjmować inną kanwę jako źródło.

Dzięki temu usunięcie wyróżnienia nie powoduje ponownego rysowania głównego rysunku: zamiast tego możemy wyczyścić rysunek na ekranie, a następnie skopiować główny rysunek na widoczny. Kopiowanie płótna jest tanie, a rysowanie drogie. Przenoszenie wyróżnień na osobne płótno pozwala uniknąć kosztów włączania i wyłączania wyróżnień.

Wszechstronne przetestowanie analizy logu czasu

Jedną z zalet tworzenia nowej funkcji od podstaw jest to, że możesz przeanalizować wcześniejsze decyzje techniczne i wprowadzić ulepszenia. Jedną z rzeczy, którą chcieliśmy ulepszyć, było wyraźne podzielenie kodu na 2 prawie całkowicie odrębne części:

Przeanalizuj plik śledzący i wyciągnij z niego potrzebne dane. Renderowanie zestawu ścieżek.

Przechowywanie analizy (część 1) niezależnie od prac nad interfejsem (część 2) pozwoliło nam stworzyć solidny system analizy. Każdy log czasu jest przeprowadzany przez szereg modułów obsługi odpowiedzialnych za różne problemy. LayoutShiftHandler oblicza wszystkie informacje potrzebne do przesunięć układu, a NetworkRequestsHandler zajmuje się wyłącznie pobieraniem żądań sieciowych. Posiadanie tego wyraźnego etapu analizowania, w którym mamy różne moduły odpowiedzialne za różne części ścieżki, również przyniosło korzyści: analizowanie ścieżki może być bardzo skomplikowane, a skupienie się na jednym problemie naraz pomaga.

Udało nam się też kompleksowo przetestować analizowanie śladów, wykonując nagrania w Narzędziach deweloperskich, zapisując je, a następnie wczytując w ramach zestawu testów. To świetna rzecz, ponieważ możemy testować na podstawie rzeczywistych śladów, zamiast tworzyć ogromne ilości fałszywych danych śladu, które mogą się zdezaktualizować.

Testowanie zrzutów ekranu interfejsu kanwy

Wracając do tematu testowania, zwykle testujemy nasze komponenty front-endu, renderując je w przeglądarce i sprawdzając, czy działają zgodnie z oczekiwaniami. Możemy wysyłać zdarzenia kliknięcia, aby wywołać aktualizacje, i sprawdzić, czy DOM generowany przez komponenty jest poprawny. To podejście sprawdza się w naszym przypadku, ale nie działa w przypadku renderowania na płótnie. Nie ma możliwości sprawdzenia płótna i określenia, co jest na nim narysowane. Dlatego nasze zwykłe podejście polegające na renderowaniu i następnym wysyłaniu zapytania nie jest odpowiednie.

Aby uzyskać pewien zakres testów, zdecydowaliśmy się na testowanie zrzutów ekranu. Każdy test uruchamia płótno, renderuje ścieżkę, którą chcemy przetestować, a następnie wykonuje zrzut ekranu elementu na płótnie. Zrzut ekranu jest następnie przechowywany w naszej bazie kodu, a przy kolejnych testach porównujemy go ze zrzutem ekranu wygenerowanym przez test. Jeśli zrzuty ekranu są różne, test się nie powiedzie. Udostępniamy też flagę do uruchomienia testu i wymuszenia aktualizacji zrzutu ekranu, gdy celowo zmienimy sposób renderowania i konieczne będzie zaktualizowanie testu.

Testy zrzutów ekranu nie są idealne i trochę nieprecyzyjne. Można tylko sprawdzić, czy cały komponent jest renderowany zgodnie z oczekiwaniami, a nie bardziej szczegółowe twierdzenia. Początkowo nadużywaliśmy tych testów, aby mieć pewność, że każdy pojedynczy komponent (HTML lub kanwa) jest renderowany prawidłowo. To spowodowało znaczne spowolnienie naszego zestawu testów i doprowadziło do problemów, w których przypadku drobne, prawie nieistotne zmiany w interfejsie (takie jak subtelne zmiany kolorów czy dodanie marginesu między elementami) powodowały, że wiele zrzutów ekranu nie było udanych i wymagało aktualizacji. Ograniczyliśmy już wykorzystanie zrzutów ekranu i zastosowaliśmy je wyłącznie w komponentach tworzonych na bazie obiektów canvas. Jak dotąd to rozwiązanie działało dobrze.

Podsumowanie

Tworzenie nowego panelu ze statystykami wydajności było dla zespołu bardzo przyjemnym, pouczającym doświadczeniem. Dowiedzieliśmy się wielu rzeczy o plikach śledzonych, pracy z płótnem i nie tylko. Mamy nadzieję, że spodoba Ci się nowy panel. Chętnie poznamy Twoją opinię.

Więcej informacji o panelu Statystyki skuteczności znajdziesz w artykule Statystyki skuteczności: przydatne statystyki dotyczące skuteczności witryny.

Pobierz kanały podglądu

Rozważ użycie przeglądarki Chrome Canary, Dev lub Beta jako domyślnej przeglądarki deweloperskiej. Te kanały wersji wstępnej zapewniają dostęp do najnowszych funkcji DevTools, umożliwiają testowanie najnowocześniejszych interfejsów API platformy internetowej i pomagają znaleźć problemy w witrynie, zanim zrobią to użytkownicy.

Kontakt z zespołem Narzędzi deweloperskich w Chrome

Aby omówić nowe funkcje, aktualizacje lub inne kwestie związane z Narzędziami deweloperskimi, skorzystaj z tych opcji.