Wyślij zapytanie Dołącz do Sii

W dobie wszechobecnych stron i aplikacji wydajność stała się kluczowym elementem, który zapewnia użytkownikowi doskonałe doświadczenie korzystania z systemu. React jest biblioteką frontendową, znaną ze swojej elastyczności oraz wydajności. Należy jednak pamiętać, że wydajność ta zależy w dużym stopniu od developera, który tworzy aplikacje, szczególnie jeśli jest to większy projekt.

W artykule skupimy się nad poznaniem i zrozumieniem wyzwań przy tworzeniu aplikacji oraz poznamy sposoby, jak sobie z nimi radzić.

Dlaczego wydajność jest tak ważna?

Jak już wspomniałem, wydajność wpływa na doświadczenie użytkownika korzystającego z naszej aplikacji. Wyobraźmy sobie sytuację, że wchodzimy na stronę, gdzie przeliczamy spory pakiet danych i aktualizujemy je co jakiś czas. Zbyt długie oczekiwanie na wynik, lub ewentualnie crash, może spowodować, że użytkownik więcej nie skorzysta z naszego rozwiązania.

Ponadto, aspekty szybkości ładowania strony mają bezpośredni wpływ na SEO (warto wspomnieć, pisząc artykuł o React, że biblioteka ta nie jest najlepszym wyborem z punktu widzenia SEO). Dlatego kwestia wydajności jest nie tylko kwestią techniczną, z którą my – programiści – musimy sobie radzić. Jest również kluczową kwestią dla biznesu, który na końcu weryfikuje rentowność przedsięwzięcia na bazie konwersji.

Wyzwania związane z wydajnością w React

Jakie są podstawowe wyzwania związane z wydajnością w React? Przede wszystkim:

  • re-rendering, nadmierny lub nieoptymalny rendering komponentów może mieć wpływ na prędkość aplikacji, zwłaszcza jeśli pracujemy na dużych danych;
  • większy rozmiar paczek pobieranych przez użytkownika wydłuża czas ładowania strony;
  • wirtualizacja dłuższych list;
  • używanie atrybutu key, gdzie jest to potrzebne;
  • kompresja zdjęć.

Do tej listy dodać można wiele innych, mniejszych elementów, które postaram się przybliżyć w poniższym artykule. A więc zacznijmy od poznania pierwszej kwestii, którą jest…

Memoizacja

Kluczową kwestią, na którą mamy wpływ, tworząc aplikacje z użyciem React, jest memoizacja. Memoizacja w React to technika optymalizacyjna polegająca na zapamiętywaniu wyników drogich w obliczeniu funkcji lub renderowania komponentów, aby uniknąć niepotrzebnego powtarzania tych operacji przy kolejnych renderowaniach.

Na szczęście React dostarcza nam gotowe narzędzia, aby poradzić sobie z tym problemem. Naszym zadaniem jest korzystanie z tych narzędzi, aby od samego początku dbać o kontrolowany re-rendering naszych komponentów. Tymi narzędziami są:

React.memo

React.memo to metoda dostarczona bezpośrednio w bibliotece React, która otacza przekazany do metody komponent i zapobiega jego niepotrzebnemu ponownemu renderowaniu, gdy jego propsy nie zmieniają się. Działa przez porównanie poprzednich i aktualnych propsów, a wykonuje renderowanie tylko wtedy, gdy wykryje zmiany.

W momencie, kiedy mamy do czynienia z komponentami o dużej ilości obliczeń lub liście wielu elementów, może mieć to spory wpływ na prędkość działania aplikacji, z uwagi na to, że pozbywamy się ponownego re-renderu kiedy nie jest on potrzebny. Poniżej znajdziemy przykład użycia memo.

fragment kodu

Na wyżej załączonym przykładzie widzimy 2 komponenty

  • Users, który generuje listę na bazie otrzymanej tablicy,
  • App, która zawiera stan counter oraz komponent Users.

Dzięki rapowaniu komponentu Users przez memo, każdy update stanu counter w komponencie App nie spowoduje renderu listy, ponieważ lista ta się nie zmienia. W innym przypadku komponent za każdym razem inicjalizowałby się na nowo.

UseMemo

Będąc już przy React.memo, wspomnijmy o hooku useMemo. Cały mechanizm jest tutaj dość podobny, lecz w tym przypadku obserwujemy zmiany, jakie zachodzą w zmiennej, a nie w komponencie.

Może być to przydatne, kiedy np. mamy komponent, który wyświetla informacje o użytkowniku, a jedna z tych danych wymaga dość skomplikowanej kalkulacji. Dzięki useMemo możemy ją zapamiętać. Jeżeli nie zmienią się parametry, które wskażemy w tablicy zależności, zapamiętana poprzednio wartość zostanie zwrócona bez ponownej kalkulacji.

fragment kodu

W załączonym przykładzie kodu prezentujemy informacje o użytkowniku, w tym status aktywności jego konta oraz wynik skomplikowanej kalkulacji zależnej od stanu konta, która wymaga znacznego czasu obliczeń.

Aby uniknąć powtarzania kodu, przedstawiamy dwa podejścia do obliczeń:

  • standardowe,
  • wykorzystujące useMemo.

Gdy użytkownik zmienia status aktywności konta poprzez kliknięcie przycisku, nie chcemy ponownie przeliczać zmiennej calculation, jeśli zmienna userMoney pozostaje niezmieniona, gdyż wynik obliczeń pozostanie taki sam.

W tradycyjnym podejściu wartość ta jest niepotrzebnie przeliczana za każdym razem, co wydłuża czas ponownego renderowania. Natomiast dzięki useMemo, wartość ta jest obliczana tylko raz i jeśli żaden z parametrów przekazanych do tablicy zależności się nie zmienia, React używa zapamiętanej wartości bez ponownego wywoływania kosztownej funkcji somePowerfulMoneyCalculation.

Efekt użycia obu podejść jest zilustrowany na dołączonych obrazkach:

Efekt zastosowania podejścia wykorzystującego useMemo
Ryc. 1 Efekt zastosowania podejścia wykorzystującego useMemo
Efekt zastosowania podejścia  bez  użycia useMemo
Ryc. 2 Efekt zastosowania podejścia bez użycia useMemo

useCallback

Ostatnim narzędziem do memoizacji w React jest hook useCallback, który służy do zapamiętywania definicji funkcji. Zasada użycia useCallback jest analogiczna do useMemo: jako pierwszy parametr przekazujemy funkcję, która ma zostać zapamiętana, a drugi to tablica zależności, które wskazuje po zmianie jakich parametrów ma na nowo zdefiniować i zapamiętać funkcję.

fragment kodu

W przedstawionym fragmencie kodu znajduje się mały formularz, który po naciśnięciu przycisku „Send” wysyła dane do backendu. Gdy w kodzie wykorzystujemy hook useCallback, nasza funkcja wysyłająca dane jest zdefiniowana tylko raz i zostaje zapamiętana, co jest efektywne, gdyż nie ma potrzeby jej wielokrotnej definicji.

Z drugiej strony, jeśli usuniemy useCallback, funkcja ta będzie definiowana ponownie za każdym razem, gdy nastąpi zmiana stanu formularza, na przykład zmiany wartości zmiennej someFlag.

Lazy loading

Na początku rozwoju nowego projektu, warto zatrzymać się na chwilę i przemyśleć, jak duża będzie nasza aplikacja i czy wszystkie jej elementy oraz moduły będą nam potrzebne od razu. Jeśli nie, warto zastanowić się nad lazy loadingiem. Jest to sposób na stopniowe ładowanie modułów aplikacji wtedy, kiedy będą nam one potrzebne. Oznacza to w praktyce, że jeśli użytkownik nie wejdzie w np.: podstronę subskrypcja, kod do tego modułu nie zostanie zaciągnięty do naszej aplikacji, co ma bezpośredni wpływ na ładowanie aplikacji.

Lazy loading możemy w bardzo prosty sposób zaimplementować do naszej aplikacji. Będzie nam do tego potrzebna metoda lazy, która dostarczana jest przez React, oraz połączenie jej z komponentem Suspence, pozwalającym określić zachowanie aplikacji podczas ładowania modułu.

Przykładowy fragment kodu:

fragment kodu

W tym kodzie implementujemy routing połączony z lazy loadingiem. Dzięki temu, w zależności od tego, na jakiej stronie się znajdujemy, pozostałe podstrony (czyli ich komponenty) będą ładowane w momencie wejścia w konkretny link. Cały proces ładowania tzw. chunków widoczny będzie w sekcji network w devtools jako kolejna, dodatkowa paczka kodu js.

Wirtualizacja

Każdy z nas na pewno miał kiedyś styczność z listami, tabelkami lub innymi komponentami, które wyświetlić miały X elementów. W przypadku, gdy obsługujemy paginacje, nie ma z tym problemu – możemy pobierać strona po stronie dane i nie grozi nam problem z obciążeniem aplikacji. Czasem jednak celem jest np.: infinity scroll, jak na Facebooku, który pozwala nam ładować nowe posty cały czas oraz wrócić do tych, które pobraliśmy parę sekund temu (a czasem nawet minut, jak ktoś się zasiedzi).

Pytanie, jakie należy postawić, to: w jaki sposób jesteśmy w stanie wyświetlić np. 10 000 elementów i dalej zapewnić płynność działania aplikacji?

Aby poradzić sobie z tym problemem, przytoczę temat wirtualizacji. Polega ona na dynamicznym ładowaniu i renderowaniu tylko tych części danych, które aktualnie są widoczne dla użytkownika. Efektem jest zmniejszona ilość elementów w DOM w danym momencie i większa płynność naszej aplikacji.

Aby w prosty sposób zaimplementować mechanizm wirtualizacji w naszej aplikacji, skorzystać możemy z zewnętrznej biblioteki o nazwie react-window. Jej użycie jest bardzo proste i już po kilku chwilach możemy zobaczyć zalety korzystania z wirtualizacji.

fragment kodu
Zastosowanie wirtualizacji
Ryc. 3 Zastosowanie wirtualizacji

Key

Kolejnym aspektem, który może zwiększyć wydajność strony, kiedy renderujemy listę, jest property key. React używa tej właściwości do śledzenia, które elementy się zmieniły, co jest szczególnie ważne przy aktualizacji stanu. Bez unikalnego (to ważne) property key, React może nieprawidłowo odtworzyć i zaktualizować elementy na liście, co prowadzi do nieefektywnego i błędnego działania aplikacji.

Posłużmy się przykładem: mamy listę 2 elementów, lecz wyobraźmy sobie, że ta lista jest dużo dłuższa. Przy mapowaniu elementów, każdy element powinien mieć swoją unikalną wartość key (nie należy stosować indexu listy). Dobrym pomysłem jest wykorzystanie jako wartości key, ID użytkownika, które, jak samo zastosowanie wskazuje, powinna być unikalne.

fragment kodu

W powyższym kodzie należy również zauważyć, że mamy metodę, której zadaniem jest usunięcie konkretnego użytkownika z naszej listy. Bez właściwego użycia key (np. poprzez index listy), React miałby spory problem na identyfikację, które elementy zostały usunięte, a które powinny zostać.

Throttling i debouncing

Obie techniki służą do wywołań funkcji w odpowiedzi na zdarzenia w naszym systemie. W skrócie:

  • Throttling – polega na ograniczeniu liczby wywołań funkcji do jednego w określonym interwale czasowym. Jest to przydatne m.in.: przy śledzeniu zdarzeń przewijania, aby uniknąć nadmiernego obciążenia. Znaczy to, że jeśli ustawimy interwał na 1s przy evencie scroll, metoda ta, mimo dalszego scrollowania, zostanie wywołana dopiero po upływie 1s.
  • Debouncing – polega na odłożeniu wykonania funkcji do momentu, gdy przestaną napływać nowe zdarzenia przez określony czas. Jest to przydatne, na przykład, przy wyszukiwaniu w czasie rzeczywistym, gdy chcemy opóźnić zapytanie do serwera do momentu, gdy użytkownik skończy pisać na klawiaturze.

Obie te metody dostępne są np. w bibliotece lodash.

Kompresja zdjęć

Niezależnie od tego, czy używamy React, Angular, Vue, czy też piszemy w czystym stacku HTML/CSS/JS, ładowanie zdjęć prawie w każdej aplikacji wyglądać będzie tak samo. Aby zapewnić szybkie ładowanie plików z serwera, warto kompresować wszystkie zdjęcia i ikony na naszej stronie. W ten sposób jesteśmy w stanie zmniejszyć rozmiar plików niekiedy nawet o 90%.

Przydatnymi narzędziami do kompresji mogą być np. tinypng lub svgomg.

Podsumowanie

Jak widać, mamy sporo możliwości na optymalizację naszych aplikacji. W tym artykule przedstawiłem kilka z nich, lecz lista może być znacznie dłuższa.

Przed startem każdego projektu warto zatrzymać się na chwilę i zastanowić m.in.:

  • jak duży będzie to projekt,
  • co będzie zawierał,
  • czy musimy wszystkie moduły ładować na raz,
  • w jaki sposób będę zarządzać stanem w aplikacji i wiele innych.

Pozwoli nam to zaplanować mechanizmy i reguły, które wesprą optymalizację aplikacji, zanim będzie za późno.

***

Jeśli interesuje Cię temat React, sprawdź również inne artykuły naszych ekspertów.

5/5 ( głosy: 4)
Ocena:
5/5 ( głosy: 4)
Autor
Avatar
Filip de Tillier

Absolwent Politechniki Gdańskiej. W 2017 roku rozpoczął swoją karierę zawodową głównie jako Front-end developer. Ma również doświadczenie jako Back-end developer z użyciem języka JavaScript oraz Python. Aktualnie skupia się nad rozwojem w bibliotece React. Jego pasją jest sport i F1.

Zostaw komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *

Może Cię również zainteresować

Pokaż więcej artykułów

Bądź na bieżąco

Zasubskrybuj naszego bloga i otrzymuj informacje o najnowszych wpisach.

Otrzymaj ofertę

Jeśli chcesz dowiedzieć się więcej na temat oferty Sii, skontaktuj się z nami.

Wyślij zapytanie Wyślij zapytanie

Natalia Competency Center Director

Get an offer

Dołącz do Sii

Znajdź idealną pracę – zapoznaj się z naszą ofertą rekrutacyjną i aplikuj.

Aplikuj Aplikuj

Paweł Process Owner

Join Sii

ZATWIERDŹ

This content is available only in one language version.
You will be redirected to home page.

Are you sure you want to leave this page?