W Chrome 102 w Narzędziach deweloperskich znajdziesz nowy eksperymentalny panel Statystyki wydajności. W tym poście omówimy nie tylko to, dlaczego pracowaliśmy nad nowym panelem, ale też wyzwania techniczne, przed którymi staliśmy, i podjęte przez nas decyzje.
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 wydajności, trudno Ci będzie określić, na co dokładnie zwrócić uwagę i które części 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ć. Na podstawie statystyk możesz wykrywać 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 Twojej witrynie, a w szczególności na wyniki podstawowych wskaźników internetowych. Oprócz oznaczenia problemów Statystyki wydajności będą zawierać praktyczne sugestie dotyczące zwiększania wyników CWV oraz linki do dodatkowych zasobów i dokumentacji.
Ten panel jest w wersji eksperymentalnej i chcielibyśmy poznać 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. 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 chodzi tylko o rysowanie odpowiednich szczegółów we właściwym miejscu, ale też o zarządzanie zdarzeniami myszy (np. gdzie użytkownik kliknął w obszarze roboczym? Czy kliknęli zdarzenie, które narysowaliśmy?) i sprawdź, czy skutecznie ponownie renderujemy płótno.
Wiele ścieżek na jednym płótnie
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.
Początkowo chcieliśmy, aby każda z tych ścieżek miała swój własny <canvas>
, tak aby widok główny składał się z wielu elementów kanwy ułożonych pionowo. Uprościłoby to renderowanie na poziomie ścieżki, ponieważ każda ścieżka mogłaby być renderowana osobno, 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 renderowaniem każdej ścieżki rysujemy prostokąt reprezentujący widoczne okno ścieżki. Dzięki temu ścieżki narysowane poza te granice zostaną przycięte przez płótno.
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ć nad ścieżką tak, jakbyśmy renderowali ją w ustawieniach (0, 0), a nasz menedżer ścieżek przetłumaczyłby ją, gdy renderuje każdą ścieżkę, aby zapewnić prawidłowe renderowanie każdej ścieżki poniżej poprzedniej.
Płótna poza ekranem dla ścieżek i fragmentów
Renderowanie na płótnie jest stosunkowo kosztowne, dlatego chcemy zapewnić Ci płynną i szybką pracę z panelem statystyk. Czasami nie da się uniknąć ponownego renderowania całego płótna. Na przykład, jeśli zmienisz poziom powiększenia, musimy zacząć od początku i ponownie wszystko wyrenderować. Ponownie renderowanie obrazu jest szczególnie kosztowne, ponieważ nie można renderować tylko jego części. Trzeba wyczyścić całą powierzchnię i ponownie ją 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órym wystąpiły problemy z wizualizacją, 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 musimy usunąć wyróżnienie: jedyną opcją jest ponowne narysowanie wszystkiego. Nie można po prostu narysować ponownie obszaru, w którym znajdowało się wyróżnienie (nie bez ogromnych zmian architektonicznych), ale ponowne narysowanie całego obrazu tylko dlatego, że chcemy usunąć niebieską obwódkę wokół jednego elementu, wydawało się przesadą. 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 interfejs na 2 pozaekranowe obszary robocze: „główny”, w którym renderowane są ścieżki, oraz „wyróżnienia”, w którym są rysowane wyróżnienia. Następnie renderujemy te płótna, kopiując je na jedną płótno widoczną 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ń.
kompleksowe testowanie analizy dzienników.
Jedną z zalet tworzenia nowej funkcji od podstaw jest to, że możesz przeanalizować wcześniejsze decyzje techniczne i wprowadzić ulepszenia. Jednym z elementów, które 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.
Oddzielenie parsowania (część 1) od pracy nad interfejsem (część 2) pozwoliło nam stworzyć solidny system parsowania. Każde śledzenie jest wykonywane przez serię obsług, które odpowiadają za różne kwestie: LayoutShiftHandler
oblicza wszystkie informacje potrzebne do zmian układu, a NetworkRequestsHandler
zajmuje się wyłącznie wyodrębnianiem żądań sieciowych. Posiadanie 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: analiza ścieżki może być bardzo skomplikowana, 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 jako część zestawu testów. To świetna rzecz, ponieważ możemy testować na podstawie rzeczywistych śladów, zamiast tworzyć ogromnych ilości danych śladów, 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ę, która umożliwia przeprowadzenie testu i wymuszenie aktualizacji zrzutu ekranu, gdy celowo zmienimy renderowanie i będzie trzeba zaktualizować test.
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. Zmniejszyliśmy wykorzystanie zrzutów ekranu i używamy ich tylko w przypadku komponentów na płótnie. Do tej pory ta równowaga sprawdza się dobrze.
Podsumowanie
Tworzenie nowego panelu Statystyki wydajności było dla naszego zespołu bardzo przyjemnym i edukacyjnym doświadczeniem. Dowiedzieliśmy się wielu rzeczy o plikach śledzonych, pracy z płótnem i nie tylko. Mamy nadzieję, że nowy panel Ci się spodoba. Czekamy na Twoją opinię.
Więcej informacji o panelu Statystyki skuteczności znajdziesz w artykule Statystyki skuteczności: przydatne statystyki dotyczące skuteczności witryny.
Pobieranie kanałów podglądu
Rozważ użycie jako domyślnej przeglądarki deweloperskiej wersji Canary, Dev lub Beta przeglądarki Chrome. 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.
- Przesyłaj opinie i prośby o dodanie funkcji na stronie crbug.com.
- Zgłoś problem z Narzędziami deweloperskimi, klikając Więcej opcji > Pomoc > Zgłoś problem z Narzędziami deweloperskimi w Narzędziach deweloperskich.
- Wyślij tweeta do @ChromeDevTools.
- Dodaj komentarze do filmów w YouTube z serii „Co nowego w Narzędziach deweloperskich” lub Wskazówki dotyczące Narzędzi deweloperskich.