Jak najłatwiej zidentyfikować wąskie gardła w wydajności renderowania React?
Mam problem z identyfikacją wąskich gardeł w wydajności renderowania podczas pracy z przeglądarką JSON. Z kilkoma elementami działa dobrze, ale w pewnym momencie staje się irytująco powolny.
Po sprawdzeniu programu profilującego wydaje się, że elementy renderują się wystarczająco szybko, ale zauważyłem kilka problemów, z którymi nie wiem, jak je rozwiązać.
Przegląd
- Aplikacja jest przeglądarką JSON, która umożliwia rozwijanie / minimalizowanie wszystkich elementów jednocześnie, a także poszczególnych elementów.
- Wydajność jest dobra z kilkoma elementami, ale wydaje się, że dramatycznie spada wraz ze wzrostem liczby elementów.
- Podczas profilowania zarówno mojej metody filtrowania obiektów za pomocą,
performance.now()
jak i sprawdzania czasu renderowania w React DevTools, liczby wydają się w porządku. Mogę to źle zinterpretować. - Próbowałem używać
React.memo()
na elementach bezstanowych (szczególnie klucz / wartość, który jest najczęściej renderowanym składnikiem), ale nie wydaje się, aby zauważalnie poprawiał wydajność. Trzeba przyznać, że nie jestem pewien, czy wystarczająco rozumiem uzasadnienie zapamiętywania komponentów Reacta, aby zaimplementować to pożytecznie.
Realizacja
- Obecnie moja aplikacja ładuje dane do elementu nadrzędnego, który przekazuje do komponentu, który ładuje drzewo JSON przy użyciu elementu rekurencyjnego.
- Ładowanie kanału JSON z adresu URL zmienia stan komponentu nadrzędnego, który jest filtrowany przy użyciu metody pomocniczej, która wykorzystuje wartości wprowadzone w polu wejściowym.
Problemy
Istnieją dwie funkcje, które odtwarzają długi czas odpowiedzi w (niezbyt dużych) dokumentach JSON:
- Przycisk rozwiń wszystko
- Kilka pierwszych naciśnięć klawiszy w zapytaniu filtrującym
W obecnej implementacji zarówno filtrowanie, jak i rozszerzanie wszystkich wyzwala display: none
zmianę elementów podrzędnych, a zachowanie prowadzi mnie do przekonania, że robię coś nieefektywnie, aby obsłużyć ten przypadek użycia.
Kroki reprodukcji
Kod jest dostępny tutaj: https://codesandbox.io/s/react-json-view-4z348
Z wersją produkcyjną tutaj (nie działającą lepiej): https://csb-4z348.vercel.app/
Aby odtworzyć problem, pobaw się funkcją Expand All (znak plus obok wejścia filtru) i niektórymi wejściami filtrującymi.
Następnie spróbuj załadować kanał JSON z większą liczbą elementów (możesz przetestować na moim kanale API GitHub ) i spróbuj filtrować / rozszerzać wszystko. Zwróć uwagę na duży hit wydajnościowy.
Co zauważyłem
- Podczas rejestrowania useEffect minimalizacja wydaje się powodować ~ 2x więcej ponownych renderowań niż rozszerzenie all.
- Gdy dane wejściowe filtra stają się bardziej szczegółowe, wydajność (logicznie) poprawia się, gdy renderowanych jest mniej elementów.
Pytanie
Chociaż byłbym wdzięczny za posunięcie się we właściwym kierunku w tym konkretnym przypadku, najbardziej ciekawi mnie, jak najlepiej zidentyfikować, co powoduje te problemy z wydajnością.
Zajrzałem do okienkowania danych wyjściowych, ale nie jest to mój pierwszy wybór i jestem prawie pewien, że robię coś źle, a nie przyczyną jest zbyt wiele renderowanych elementów.
Doceniam poświęcony czas iz góry dziękuję za wszelkie wskazówki, których możesz udzielić!
Odpowiedzi
Wygląda na to, że odpowiedziałem na własne pytanie. Problem polegał na problemie z uzgadnianiem z powodu użycia identyfikatora UUID jako kluczowego elementu w moich komponentach podrzędnych, co powodowało ich ponowne renderowanie za każdym razem, gdy zmienił się stan minimalizacji. Z dokumentów:
Klucze powinny być stabilne, przewidywalne i niepowtarzalne. Niestabilne klucze (takie jak te produkowane przez Math.random ()) spowodują niepotrzebne odtworzenie wielu instancji komponentów i węzłów DOM, co może spowodować obniżenie wydajności i utratę stanu komponentów potomnych.
Zostawię tutaj kroki dla każdego, kto napotka ten problem.
Po (zbyt długim) kopaniu w profilerze wydajności zauważyłem, że za każdym razem, gdy minimalizowałem lub rozbudowywałem elementy, każde dziecko było ponownie montowane. Po konsultacji z Google w sprawie bardziej szczegółowego zapytania, znalazłem ten post na blogu i zdałem sobie sprawę, że popełniam ten rażący błąd wydajności.
Kiedy znalazłem źródło problemu, znalazłem wiele innych odniesień do niego.
Po naprawieniu kluczowego rekwizytu czas interakcji skrócił się o ~ 60%, aby zminimalizować / rozwinąć wszystko.
Na koniec zapamiętałem kilka innych komponentów związanych z filtrem natychmiastowym i ostatecznie wydaje się, że działa tak dobrze, jak bym chciał na razie.
Dziękuję wszystkim, którzy w międzyczasie się temu przyjrzeli i mam nadzieję, że jest to pomocne dla każdego, kto może się z tym spotkać.