Metody poprawy i optymalizacji wydajności w aplikacjach React

Opublikowany: 2022-03-10
Szybkie podsumowanie ↬ Od czasu wprowadzenia React zmienił sposób, w jaki programiści front-end budują aplikacje internetowe, a jego wirtualny DOM słynie z efektywnego renderowania komponentów. W tym samouczku omówimy różne metody optymalizacji wydajności w aplikacjach React, a także funkcje Reacta, których możemy użyć do poprawy wydajności.

React umożliwia aplikacjom internetowym szybką aktualizację interfejsów użytkownika (UI), ale to nie znaczy, że Twoja średnia lub duża aplikacja React będzie działać wydajnie. Jego wydajność będzie zależeć od tego, w jaki sposób korzystasz z Reacta podczas jego tworzenia, a także od zrozumienia, jak działa React i procesu, w którym komponenty przechodzą przez różne fazy swojego cyklu życia. React oferuje wiele ulepszeń wydajności aplikacji internetowych, które można osiągnąć za pomocą różnych technik, funkcji i narzędzi.

W tym samouczku omówimy różne metody optymalizacji wydajności w aplikacjach React, a także funkcje Reacta, których możemy użyć do poprawy wydajności.

Od czego zacząć optymalizację wydajności w aplikacji React?

Nie możemy rozpocząć optymalizacji aplikacji, nie wiedząc dokładnie, kiedy i gdzie przeprowadzić optymalizację. Możesz zapytać: „Od czego zaczynamy?”

Podczas początkowego procesu renderowania React buduje drzewo DOM komponentów. Tak więc, gdy dane ulegną zmianie w drzewie DOM, chcemy, aby React ponownie wyrenderował tylko te komponenty, na które wpłynęła zmiana, pomijając pozostałe komponenty w drzewie, na które nie wpłynęła zmiana.

Jednak React może ponownie wyrenderować wszystkie komponenty w drzewie DOM, nawet jeśli nie dotyczy to wszystkich. Spowoduje to wydłużenie czasu ładowania, zmarnowanie czasu, a nawet zmarnowanie zasobów procesora. Musimy temu zapobiec. Na tym więc skoncentrujemy nasze wysiłki optymalizacyjne.

W tej sytuacji moglibyśmy skonfigurować każdy komponent tak, aby renderował lub różnicował tylko wtedy, gdy jest to konieczne, aby uniknąć marnowania zasobów i czasu.

Więcej po skoku! Kontynuuj czytanie poniżej ↓

Pomiar wydajności

Nigdy nie rozpoczynaj procesu optymalizacji swojej aplikacji React w oparciu o to, co czujesz. Zamiast tego użyj dostępnych narzędzi pomiarowych, aby przeanalizować wydajność swojej aplikacji React i uzyskać szczegółowy raport o tym, co może ją spowalniać.

Analiza komponentów React za pomocą karty wydajności w przeglądarce Chrome

Zgodnie z dokumentacją Reacta, gdy jesteś jeszcze w trybie programistycznym, możesz użyć zakładki „Wydajność” w przeglądarce Chrome, aby zwizualizować, jak komponenty React montują, aktualizują i odmontowują. Na przykład poniższy obrazek pokazuje profilowanie na karcie „Wydajność” przeglądarki Chrome i analizowanie mojego bloga w trybie programistycznym.

Podsumowanie profilera wydajności
Podsumowanie profilera wydajności (duży podgląd)

Aby to zrobić, wykonaj następujące kroki:

  1. Wyłącz tymczasowo wszystkie rozszerzenia, zwłaszcza React Developer Tools, ponieważ mogą zepsuć wynik analizy. Możesz łatwo wyłączyć rozszerzenia, uruchamiając przeglądarkę w trybie incognito.
  2. Upewnij się, że aplikacja działa w trybie deweloperskim. Oznacza to, że aplikacja powinna działać na twoim lokalnym hoście.
  3. Otwórz Narzędzia programistyczne Chrome, kliknij kartę „Wydajność”, a następnie kliknij przycisk „Nagraj”.
  4. Wykonaj czynności, które chcesz profilować. Nie nagrywaj dłużej niż 20 sekund, w przeciwnym razie Chrome może się zawiesić.
  5. Zatrzymaj nagrywanie.
  6. Zdarzenia React zostaną zgrupowane pod etykietą „Czas użytkownika”.

Liczby z profilera są względne. W większości przypadków i komponenty będą renderowane szybciej w produkcji. Niemniej jednak powinno to pomóc w ustaleniu, kiedy interfejs użytkownika jest aktualizowany przez pomyłkę, a także jak głębokie i jak często występują aktualizacje interfejsu użytkownika.

React Developer Tools Profiler

Zgodnie z dokumentacją React, w reakcjach React react-dom 16.5+ i React react-native 0.57+ rozszerzone możliwości profilowania są dostępne w trybie programisty przy użyciu React Developer Tools Profiler. Profiler używa eksperymentalnego interfejsu API Profiler React do zestawiania informacji o czasie o każdym renderowanym składniku w celu zidentyfikowania wąskich gardeł wydajności w aplikacji React.

Po prostu pobierz React Developer Tools dla swojej przeglądarki, a następnie możesz użyć narzędzia profilującego, które jest z nim dostarczane. Profiler może być używany tylko w trybie programistycznym lub w kompilacji profilu produkcyjnego React v16.5+. Poniższy obrazek jest podsumowaniem mojego bloga w trybie deweloperskim przy użyciu React Developer Tools Profiler:

React Developer Tools Profiler Flamegraph
React Developer Tools Profiler Flamegraph (duży podgląd)

Aby to osiągnąć, wykonaj następujące kroki:

  1. Pobierz narzędzia programistyczne React.
  2. Upewnij się, że Twoja aplikacja React jest w trybie deweloperskim lub w kompilacji profilu produkcyjnego React v16.5+.
  3. Otwórz kartę „Narzędzia programistyczne” w Chrome. Dostępna będzie nowa zakładka o nazwie „Profiler”, dostarczona przez narzędzia React Developer Tools.
  4. Kliknij przycisk "Nagraj" i wykonaj czynności, które chcesz profilować. Najlepiej zatrzymać nagrywanie po wykonaniu czynności, które chcesz profilować.
  5. Wykres (znany jako flamegraph) pojawi się ze wszystkimi funkcjami obsługi zdarzeń i komponentami Twojej aplikacji React.

Uwaga : więcej informacji znajdziesz w dokumentacji.

Zapamiętywanie za pomocą React.memo()

React v16 został wydany z dodatkowym API, komponentem wyższego rzędu o nazwie React.memo() . Zgodnie z dokumentacją istnieje to tylko jako optymalizacja wydajności .

Jego nazwa „ memo ” pochodzi od memoization, która jest w zasadzie formą optymalizacji stosowaną głównie do przyspieszenia kodu poprzez przechowywanie wyników kosztownych wywołań funkcji i zwracanie przechowywanych wyników za każdym razem, gdy ta sama droga funkcja jest wywoływana ponownie.

Zapamiętywanie to technika jednorazowego wykonania funkcji, zwykle czystej funkcji, a następnie zapisania wyniku w pamięci. Jeśli spróbujemy ponownie wykonać tę funkcję, z tymi samymi argumentami, co poprzednio , zwróci ona po prostu poprzednio zapisany wynik z wykonania pierwszej funkcji, bez ponownego jej wykonywania.

Odwzorowując powyższy opis na ekosystem Reacta, wspomniane funkcje to komponenty Reacta, a argumenty to props.

Domyślnym zachowaniem komponentu zadeklarowanego przy użyciu React.memo() jest to, że renderuje on tylko wtedy, gdy właściwości w komponencie uległy zmianie. Wykonuje płytkie porównanie rekwizytów, aby to sprawdzić, ale dostępna jest opcja, aby to obejść.

React.memo() zwiększa wydajność aplikacji React, unikając ponownego renderowania komponentów, których właściwości nie uległy zmianie lub gdy ponowne renderowanie nie jest potrzebne.

Poniższy kod to podstawowa składnia React.memo() :

 const MemoizedComponent = React.memo((props) => { // Component code goes in here })

Kiedy używać React.memo()

  • Czysty składnik funkcjonalny
    Możesz użyć React.memo() , jeśli twój komponent działa, ma te same właściwości i zawsze wyświetla te same dane wyjściowe. Możesz także użyć React.memo() na nieczysto funkcjonalnych komponentach z hookami React.
  • Komponent renderuje się często
    Możesz użyć React.memo() , aby zawinąć komponent, który często się renderuje.
  • Komponent jest ponownie renderowany z tymi samymi rekwizytami
    Użyj React.memo() , aby zawinąć komponent, który zwykle jest dostarczany z tymi samymi właściwościami podczas ponownego renderowania.
  • Średnie do wysokich pierwiastków
    Użyj go dla komponentu, który zawiera średnią lub dużą liczbę elementów interfejsu użytkownika, aby sprawdzić właściwości pod kątem równości.

Uwaga : Zachowaj ostrożność podczas zapamiętywania komponentów, które wykorzystują właściwości jako wywołania zwrotne. Pamiętaj, aby między renderowaniami używać tej samej instancji funkcji zwrotnej. Dzieje się tak, ponieważ komponent nadrzędny może zapewnić różne instancje funkcji zwrotnej przy każdym renderowaniu, co spowoduje przerwanie procesu zapamiętywania. Aby to naprawić, upewnij się, że zapamiętany komponent zawsze otrzymuje tę samą instancję wywołania zwrotnego.

Zobaczmy, jak możemy wykorzystać zapamiętywanie w rzeczywistej sytuacji. Poniższy komponent funkcjonalny, zwany „Photo”, używa React.memo() , aby zapobiec ponownemu renderowaniu.

 export function Photo({ title, views }) { return ( <div> <div>Photo title: {title}</div> <div>Location: {location}</div> </div> ); } // memoize the component export const MemoizedPhoto = React.memo(Photo);

Powyższy kod składa się z funkcjonalnego komponentu, który wyświetla div zawierający tytuł zdjęcia i lokalizację obiektu na zdjęciu. Zapamiętujemy również komponent, tworząc nową funkcję i nazywając ją MemoizedPhoto . Zapamiętywanie komponentu zdjęcia zapobiegnie jego ponownemu renderowaniu, o ile rekwizyty, title i location będą takie same w kolejnych renderowaniach.

 // On first render, React calls MemoizedPhoto function. <MemoizedPhoto title="Effiel Tower" location="Paris" /> // On next render, React does not call MemoizedPhoto function, // preventing rendering <MemoizedPhoto title="Effiel Tower" location="Paris" />

Tutaj React wywołuje zapamiętaną funkcję tylko raz. Nie wyrenderuje komponentu w następnym wywołaniu, o ile właściwości pozostaną takie same.

Grupowanie i minifikacja

W jednostronicowych aplikacjach React możemy połączyć i zminimalizować cały nasz kod JavaScript w jednym pliku. To jest w porządku, o ile nasza aplikacja jest stosunkowo niewielka.

Wraz z rozwojem naszej aplikacji React łączenie i minimalizowanie całego naszego kodu JavaScript w jednym pliku staje się problematyczne, trudne do zrozumienia i nużące. Wpłynie to również na wydajność i czas ładowania naszej aplikacji React, ponieważ wysyłamy do przeglądarki duży plik JavaScript. Potrzebujemy więc jakiegoś procesu, który pomoże nam podzielić bazę kodu na różne pliki i dostarczyć je do przeglądarki w odpowiednich odstępach czasu.

W takiej sytuacji możemy użyć jakiejś formy pakietu zasobów, takiego jak Webpack, a następnie wykorzystać jego funkcję dzielenia kodu, aby podzielić naszą aplikację na wiele plików.

Podział kodu jest sugerowany w dokumentacji Webpack jako sposób na skrócenie czasu ładowania aplikacji. Jest to również sugerowane w dokumentacji Reacta do leniwego ładowania (obsługującego tylko to, czego aktualnie potrzebuje użytkownik), co może znacznie poprawić wydajność.

Webpack sugeruje trzy ogólne podejścia do dzielenia kodu:

  • Punkty wejścia
    Ręcznie podziel kod za pomocą konfiguracji wpisu.
  • Zapobieganie powielaniu
    Użyj SplitChunksPlugin do usuwania duplikatów i dzielenia porcji.
  • Import dynamiczny
    Podziel kod za pomocą wbudowanych wywołań funkcji w modułach.

Korzyści z dzielenia kodu

  • Podział kodu pomaga w korzystaniu z zasobów pamięci podręcznej przeglądarki i kodu, który nie zmienia się często.
  • Pomaga także przeglądarce równolegle pobierać zasoby, co skraca całkowity czas ładowania aplikacji.
  • Umożliwia nam dzielenie kodu na porcje, które będą ładowane na żądanie lub zgodnie z potrzebami aplikacji.
  • Dzięki temu początkowe pobieranie zasobów przy pierwszym renderowaniu jest stosunkowo niewielkie, co skraca czas ładowania aplikacji.
Proces łączenia i minifikacji
Proces łączenia i minifikacji (duży podgląd)

Niezmienne struktury danych

Dokumentacja Reacta mówi o sile niemutowania danych. Wszelkie dane, których nie można zmienić, są niezmienne. Niezmienność to koncepcja, którą programiści React powinni zrozumieć.

Niezmiennej wartości lub obiektu nie można zmienić. Tak więc, gdy następuje aktualizacja, nowa wartość jest tworzona w pamięci, pozostawiając starą nietkniętą.

Możemy użyć niezmiennych struktur danych i React.PureComponent , aby automatycznie sprawdzić, czy nie nastąpiła zmiana stanu złożonego. Na przykład, jeśli stan w aplikacji jest niezmienny, możesz w rzeczywistości zapisać wszystkie obiekty stanu w jednym sklepie za pomocą biblioteki do zarządzania stanem, takiej jak Redux, co umożliwia łatwe zaimplementowanie funkcji cofania i ponawiania.

Nie zapominaj, że nie możemy zmienić niezmiennych danych po ich utworzeniu.

Korzyści z niezmiennych struktur danych

  • Nie mają skutków ubocznych.
  • Niezmienne obiekty danych można łatwo tworzyć, testować i używać.
  • Pomagają nam napisać logikę, której można użyć do szybkiego sprawdzania aktualizacji w stanie, bez konieczności ciągłego sprawdzania danych.
  • Pomagają zapobiegać sprzężeniu czasowemu (rodzaj sprzężenia, w którym kod zależy od kolejności wykonania).

Poniższe biblioteki pomagają zapewnić zestaw niezmiennych struktur danych:

  • niezmienność-pomocnik
    Zmutuj kopię danych bez zmiany źródła.
  • Niezmienny.js
    Niezmienne, trwałe kolekcje danych dla JavaScript zwiększają wydajność i prostotę.
  • bez szwu-niezmienny
    Niezmienne struktury danych dla JavaScriptu stają się kompatybilne wstecz z normalnymi tablicami i obiektami JavaScript.
  • React-copy-write
    Daje to niezmienny stan ze zmiennym interfejsem API.

Inne metody poprawy wydajności

Użyj kompilacji produkcyjnej przed wdrożeniem

Dokumentacja React sugeruje użycie zminifikowanej wersji produkcyjnej podczas wdrażania aplikacji.

Ostrzeżenie „wersja produkcyjna” React Developer Tools
Ostrzeżenie „wersja produkcyjna” w narzędziach React Developer Tools (duży podgląd)

Unikaj funkcji anonimowych

Ponieważ funkcje anonimowe nie mają przypisanego identyfikatora (poprzez const/let/var ), nie są trwałe za każdym razem, gdy komponent nieuchronnie zostanie ponownie wyrenderowany. Powoduje to, że JavaScript alokuje nową pamięć za każdym razem, gdy ten komponent jest ponownie renderowany, zamiast przydzielać pojedynczy fragment pamięci tylko raz, na przykład w przypadku używania nazwanych funkcji.

 import React from 'react'; // Don't do this. class Dont extends Component { render() { return ( <button onClick={() => console.log('Do not do this')}> Don't </button> ); } } // The better way class Do extends Component { handleClick = () => { console.log('This is OK'); } render() { return ( <button onClick={this.handleClick}> Do </button> ); } }

Powyższy kod pokazuje dwa różne sposoby, aby przycisk wykonywał akcję po kliknięciu. Pierwszy blok kodu używa funkcji anonimowej we właściwości onClick() , co wpłynęłoby na wydajność. Drugi blok kodu używa nazwanej funkcji w funkcji onClick() , co jest poprawnym sposobem w tym scenariuszu.

Montaż i demontaż komponentów często jest kosztowny

Używanie warunków warunkowych lub tenar w celu zniknięcia komponentu (tj. odmontowania go) nie jest zalecane, ponieważ zniknięcie komponentu spowoduje odświeżenie i ponowne wlanie się przeglądarki. Jest to kosztowny proces, ponieważ pozycje i geometrie elementów HTML w dokumencie będą musiały zostać ponownie obliczone. Zamiast tego możemy użyć właściwości opacity i visibility CSS, aby ukryć komponent. W ten sposób komponent będzie nadal w DOM, ale niewidoczny, bez żadnych kosztów wydajności.

Wirtualizacja długich list

Dokumentacja sugeruje, że jeśli renderujesz listę z dużą ilością danych, powinieneś renderować niewielką część danych z listy naraz w widocznym oknie roboczym. Następnie możesz renderować więcej danych w miarę przewijania listy; w związku z tym dane są wyświetlane tylko wtedy, gdy znajdują się w rzutni. Ten proces nazywa się „okienkowaniem”. W okienkach w dowolnym momencie renderowany jest mały podzbiór wierszy. Istnieją popularne biblioteki do tego celu, z których dwie są utrzymywane przez Briana Vaughna:

  • okno reakcji
  • Reaguj-wirtualizowany

Wniosek

Istnieje kilka innych metod poprawy wydajności aplikacji React. W tym artykule omówiono najważniejsze i skuteczne metody optymalizacji wydajności.

Mam nadzieję, że podobało Ci się czytanie tego samouczka. Więcej informacji można znaleźć w poniższych zasobach. Jeśli masz jakieś pytania, zostaw je w sekcji komentarzy poniżej. Chętnie odpowiem na każde z nich.

Referencje i powiązane zasoby

  • „Optymalizacja wydajności”, React Docs
  • „Mądrze korzystaj z React.memo”, Dmitri Pavlutin
  • „Techniki optymalizacji wydajności w reakcji”, Niteesh Yadav
  • „Niezmienność w reakcji: nie ma nic złego w mutujących obiektach”, Esteban Herrera
  • „10 sposobów na zoptymalizowanie wydajności aplikacji React”, Chidume Nnamdi
  • „5 wskazówek, jak poprawić wydajność aplikacji React”, William Le