5 sposobów poprawienia wydajności aplikacji React dzięki funkcji AirSHIFT w czasie działania

Autentyczne studium przypadku dotyczące optymalizacji skuteczności React SPA.

Kento Tsuji
Kento Tsuji
Satoshi Arai
Satoshi Arai
Yusuke Utsunomiya
Yusuke Utsunomiya
Yosuke Furukawa
Yosuke Furukawa

Wydajność witryny nie zależy tylko od czasu jej wczytywania. Zapewnienie użytkownikom szybkiego działania i elastyczności jest bardzo ważne, zwłaszcza w przypadku aplikacji komputerowych, z których korzystają na co dzień. Zespół inżynierów z firmy Recruit Technologies przeprowadził projekt refaktoryzacyjny, aby ulepszyć jedną ze swoich aplikacji internetowych, AirSHIFT, i zwiększyć wydajność wprowadzania danych przez użytkowników. Zobacz, jak to zrobili.

Długi czas reakcji, mniejsza produktywność

AirSHIFT to aplikacja internetowa na komputer, która pomaga właścicielom sklepów, takich jak restauracje i kawiarnie, zarządzać pracą zmianową pracowników. Oparta na technologii React aplikacja jednostronicowa zapewnia zaawansowane funkcje klienckie, w tym różne tabele siatki z harmonogramami zmian porządkowanych według dnia, tygodnia, miesiąca i nie tylko.

Zrzut ekranu aplikacji internetowej AirSHIFT.

Gdy zespół inżynierów Recruit Technologies dodał do aplikacji AirSHIFT nowe funkcje, zaczęło się pojawiać więcej opinii o niskiej wydajności. Yosuke Furukawa, kierownik zespołu ds. inżynierii w AirSHIFT, powiedział:

Przeprowadzone badanie opinii użytkowników zaskoczyło nas, gdy pewna właścicielka sklepu stwierdziła, że po kliknięciu przycisku woli wstać i zaparzyć kawę, żeby nie czekać na załadowanie stołu.

Po przeprowadzeniu badań zespół inżynierów zauważył, że wielu użytkowników próbowało wczytywać tabele z masowymi zmianami na komputerach o niskiej wydajności, takich jak laptop 1 GHz Celeron M sprzed 10 lat.

Niekończący się wskaźnik postępu na urządzeniach słabszych.

Aplikacja AirSHIFT blokowała główny wątek za pomocą kosztownych skryptów, ale zespół inżynierów nie zdawał sobie sprawy, jak drogie są te skrypty, ponieważ były opracowywane i testowane na komputerach o zaawansowanych specyfikacjach i szybkim połączeniu Wi-Fi.

Wykres przedstawiający aktywność w czasie działania aplikacji.
Podczas wczytywania tabeli shift uruchomione skrypty poświęcały około 80% czasu wczytywania.

Po profilowaniu wydajności w Narzędziach deweloperskich w Chrome z włączonym ograniczaniem wykorzystania procesora i sieci stało się jasne, że potrzebna jest optymalizacja wydajności. Do rozwiązania tego problemu zespół AirSHIFT utworzył grupę zadaniową. Oto 5 rzeczy, na których skupili się, aby Twoja aplikacja lepiej reagowała na działania użytkowników.

1. Wirtualizacja dużych tabel

Wyświetlenie tabeli zmian wymagało wielu kosztownych kroków: skonstruowania wirtualnego DOM i wyrenderowania go na ekranie proporcjonalnie do liczby pracowników i przedziałów czasu. Jeśli na przykład restauracja ma 50 pracowników i chce sprawdzić swój miesięczny harmonogram zmian, otrzymana tabela to 50 osób (członków) pomnożona przez 30 (dni), co daje 1500 komponentów do wyrenderowania. To bardzo kosztowna operacja, zwłaszcza w przypadku urządzeń o niskiej specyfikacji. W rzeczywistości sytuacja była gorsza. Z badań wynika, że były tam sklepy zarządzające 200 pracownikami,które wymagały około 6000 komponentów w jednej miesięcznej tabeli.

Aby zmniejszyć koszt tej operacji, AirSHIFT wirtualizowała tabelę zmian. Aplikacja podłącza teraz tylko komponenty w widocznym obszarze i odłącza komponenty poza ekranem.

Zrzut ekranu z adnotacją, który pokazuje, że funkcja AirSHIFT wyrenderowała treści poza widocznym obszarem.
Przed: renderowanie wszystkich komórek tabeli shift.
Zrzut ekranu z adnotacją, który pokazuje, że AirSHIFT renderuje teraz tylko treść widoczną w widocznym obszarze.
Po: renderowaniu tylko komórek w widocznym obszarze.

W tym przypadku AirSHIFT zastosowała wirtualizację z użyciem reakcji, ponieważ istniały wymagania dotyczące włączania skomplikowanych dwuwymiarowych tabel siatki. Analizuje też sposoby przekształcenia implementacji tak, aby w przyszłości wykorzystywała uproszczony okno reakcji.

Wyniki

Samo wirtualizowanie tabeli skróciło czas wykonywania skryptów o 6 sekund (przy czterokrotnym spowolnieniu procesora i ograniczonym środowisku Macbooka Pro 3G). Była to najistotniejsza poprawa wydajności w projekcie refaktoryzacyjnym.

Zrzut ekranu z adnotacjami przedstawiający panel wydajności Narzędzi deweloperskich w Chrome.
Przed: około 10 sekund tworzenia skryptu po wprowadzeniu danych przez użytkownika.
Kolejny zrzut ekranu z adnotacjami przedstawiający panel wydajności Narzędzi deweloperskich w Chrome.
Po: 4 sekundach wykonywania skryptu po wprowadzeniu przez użytkownika.

2. Kontrola za pomocą interfejsu User Timing API

Następnie zespół AirSHIFT zrefaktoryzował skrypty uruchamiane z wykorzystaniem danych wejściowych użytkownika. Wykres płomieniowy Narzędzi deweloperskich w Chrome umożliwia analizę tego, co faktycznie dzieje się w wątku głównym. Zespół AirSHIFT ułatwił analizowanie aktywności aplikacji na podstawie cyklu życia React.

React 16 udostępnia śledzenie wydajności za pomocą interfejsu User Timing API, który można wizualizować w sekcji Timings (Czas) Narzędzi deweloperskich w Chrome. AirSHIFT użyła sekcji Czasy, aby znaleźć niepotrzebną logikę działającą w zdarzeniach cyklu życia React.

Sekcja Czasy w panelu Wydajność Narzędzi deweloperskich w Chrome.
Zdarzenia czasu działań użytkownika w React.

Wyniki

Zespół AirSHIFT odkrył, że tuż przed każdą nawigacją po trasie miało miejsce niepotrzebne uzgadnianie drzewa React. Oznaczało to, że React niepotrzebnie aktualizował tabelę shift przed rozpoczęciem nawigacji. Przyczyną problemu była niepotrzebna aktualizacja stanu Redux. Jej naprawienie zaoszczędziło około 750 ms czasu na tworzeniu skryptów. Wprowadzono też inne mikrooptymalizacje, które doprowadziły do skrócenia łącznego czasu tworzenia skryptów o 1 sekundę.

3. Leniwe ładowanie komponentów i przenoszenie kosztownych rozwiązań logicznych do pracowników sieciowych

AirSHIFT ma wbudowaną aplikację czatu. Wielu właścicieli sklepów komunikuje się z pracownikami na czacie, korzystając z tabeli zmian, co oznacza, że użytkownik może pisać wiadomość, gdy tabela się wczytuje. Jeśli wątek główny zawiera skrypty renderujące tabelę, dane wejściowe użytkownika mogą być nieprawidłowe.

Aby zwiększyć wygodę korzystania z tabeli, AirSHIFT używa teraz komponentów React.lazy i Susense do wyświetlania obiektów zastępczych zawartości tabeli oraz leniwego ładowania komponentów.

Zespół AirSHIFT przeniósł też niektóre drogie logiki biznesowe w ramach leniwego ładowania komponentów do pracowników internetowych. Rozwiązało to problem z zacinaniem się danych wejściowych użytkownika, zwolnił wątek główny, dzięki czemu mógł on skupić się na reagowaniu na to, co użytkownik chce przekazać.

Zwykle deweloperzy mają problemy z zatrudnieniem pracowników, ale tym razem Comlink wykonała dla nich najtrudniejsze zadania. Poniżej znajduje się pseudokod pokazujący, jak zespół AirSHIFT przeprowadził jedną z najdroższych operacji – obliczał łączne koszty pracy.

W App.js używaj plików React.lazy i Susense, aby podczas wczytywania wyświetlać treści zastępcze

/** App.js */
import React, { lazy, Suspense } from 'react'

// Lazily loading the Cost component with React.lazy
const Hello = lazy(() => import('./Cost'))

const Loading = () => (
  <div>Some fallback content to show while loading</div>
)

// Showing the fallback content while loading the Cost component by Suspense
export default function App({ userInfo }) {
   return (
    <div>
      <Suspense fallback={<Loading />}>
        <Cost />
      </Suspense>
    </div>
  )
}

W komponencie Koszt do wykonania logiki obliczeniowej użyj narzędzia comlink

/** Cost.js */
import React from 'react';
import { proxy } from 'comlink';

// import the workerlized calc function with comlink
const WorkerlizedCostCalc = proxy(new Worker('./WorkerlizedCostCalc.js'));
export default async function Cost({ userInfo }) {
  // execute the calculation in the worker
  const instance = await new WorkerlizedCostCalc();
  const cost = await instance.calc(userInfo);
  return <p>{cost}</p>;
}

Wdróż logikę obliczeń, która działa w instancji roboczej i udostępniaj ją za pomocą comlink

// WorkerlizedCostCalc.js
import { expose } from 'comlink'
import { someExpensiveCalculation } from './CostCalc.js'

// Expose the new workerlized calc function with comlink
expose({
  calc(userInfo) {
    // run existing (expensive) function in the worker
    return someExpensiveCalculation(userInfo);
  }
}, self);

Wyniki

Pomimo ograniczonej ilości logiki, jaką firma używała w okresie próbnym, AirSHIFT przesunęła około 100 ms kodu JavaScript z wątku głównego do wątku roboczego (przy symulowaniu czterokrotnego ograniczania procesora).

Zrzut ekranu z nagraniem panelu wydajności Narzędzi deweloperskich w Chrome, który pokazuje, że skrypty są wykonywane w strefie roboczej strony internetowej, a nie w wątku głównym.

AirSHIFT sprawdza obecnie, czy może leniwie ładować inne komponenty i przeciążyć więcej logiki pracownikom sieci, aby jeszcze bardziej ograniczyć obciążenie.

4. Ustawianie budżetu skuteczności

Po wprowadzeniu wszystkich tych optymalizacji kluczowe było dbanie o wydajność aplikacji przez dłuższy czas. AirSHIFT używa teraz atrybutu bundlesize, aby nie przekraczać obecnego rozmiaru plików JavaScript i CSS. Poza określeniem tych podstawowych budżetów zespół utworzył panel do wyświetlania różnych centyli czasu wczytywania tabeli zmian, aby sprawdzić, czy aplikacja działa nawet w nieidealnych warunkach.

  • Teraz jest mierzony czas ukończenia skryptu dla każdego zdarzenia Redux
  • Dane o skuteczności są zbierane w narzędziu Elasticsearch.
  • Skuteczność każdego zdarzenia na 10, 25, 50 i 75 centylu jest wizualizowana za pomocą narzędzia Kibana.

AirSHIFT monitoruje teraz zdarzenie wczytywania tabeli shift, aby mieć pewność, że kończy się w ciągu 3 sekund w przypadku 75 centyla użytkowników. W tej chwili jest to nieegzekwowany budżet, ale firma rozważa automatyczne otrzymywanie powiadomień przez Elasticsearch, gdy przekroczą one budżet.

Wykres pokazujący, że 75 centyl kończy się po około 2500 ms, 50 centylu około 1250 ms, 25 centyl za około 750 ms i 10 centyl w ciągu około 500 ms.
Panel Kibana przedstawiający dzienne dane dotyczące skuteczności według centylów.

Wyniki

Na podstawie powyższego wykresu widać, że AirSHIFT wykorzystuje teraz głównie 3-sekundowy budżet dla 75 centyla użytkowników i wczytuje tabelę zmian w ciągu sekundy w przypadku użytkowników z 25 centylem. Dzięki zbieraniu danych o wydajności RUM z różnych warunków i urządzeń AirSHIFT może teraz sprawdzić, czy nowa funkcja faktycznie wpływa na wydajność aplikacji.

5. Hackathony na temat wydajności

Wszystkie wysiłki w zakresie optymalizacji wydajności były ważne i skuteczne, nie zawsze jest łatwo zmusić zespoły inżynierów i zespoły biznesowe, aby potraktowały priorytetowo niefunkcjonalne projekty. Niestety część z tych optymalizacji skuteczności nie jest możliwa do planowania. Wymagają eksperymentowania oraz metody prób i błędów.

AirSHIFT przeprowadza teraz 1-dniowe wewnętrzne maratony związane z wydajnością, aby inżynierowie mogli skupić się tylko na pracy związanej z wydajnością. W tych hackathonach eliminują wszystkie ograniczenia i szanują kreatywność inżynierów, co oznacza, że warto rozważyć każde wdrożenie, które zwiększa szybkość. Aby przyspieszyć hackathon, AirSHIFT dzieli grupę na małe zespoły, a każda z nich rywalizuje o to, kto może uzyskać największy wzrost skuteczności w Lighthouse. Drużyny są bardzo rywalizujące! 🔥

Zdjęcia z hackathonu.

Wyniki

Takie podejście się sprawdza.

  • Wąskie gardła wydajności można łatwo wykryć, testując różne podejścia podczas hackathonu i mierząc je za pomocą narzędzia Lighthouse.
  • Po zakończeniu hackathonu trudno jest przekonać zespół, który optymalizację powinien potraktować priorytetowo w odniesieniu do wersji produkcyjnej.
  • To także skuteczny sposób promowania znaczenia szybkości. Każdy uczestnik może zrozumieć zależność między sposobem kodowania a jego skutecznością.

Dobrym efektem ubocznym było to, że wiele innych zespołów inżynierów w Recruit zainteresowało się tym praktycznym podejściem, a zespół AirSHIFT przeprowadził teraz w firmie liczne hackathony.

Podsumowanie

Optymalizacja w AirSHIFT z pewnością się opłaciła. Teraz AirSHIFT wczytuje tabelę zmian w medianie w ciągu 1,5 sekundy, co stanowi sześciokrotny wzrost w stosunku do wyników sprzed projektu.

Po uruchomieniu optymalizacji skuteczności jeden z użytkowników powiedział:

Dziękuję za pomoc w szybkim wczytywaniu tabeli zmian. Organizowanie pracy zmianowej jest teraz znacznie wydajniejsze.