Podział kodu za pomocą React.lazy i Susense

Nigdy nie musisz wysyłać użytkownikom więcej kodu niż jest to konieczne, więc podziel pakiety, aby uniknąć takiej sytuacji.

Metoda React.lazy ułatwia dzielenie kodu aplikacji React na poziomie komponentów za pomocą importów dynamicznych.

import React, { lazy } from 'react';

const AvatarComponent = lazy(() => import('./AvatarComponent'));

const DetailsComponent = () => (
  <div>
    <AvatarComponent />
  </div>
)

Dlaczego ta funkcja jest przydatna?

Duża aplikacja React zwykle składa się z wielu komponentów, metod narzędziowych i bibliotek innych firm. Jeśli nie podejmiesz wysiłku, aby wczytywać różne części aplikacji tylko wtedy, gdy są potrzebne, do użytkowników zostanie wysłany jeden duży pakiet JavaScriptu, gdy tylko załadują pierwszą stronę. Może to znacząco wpłynąć na wydajność strony.

Funkcja React.lazy zapewnia wbudowany sposób na rozdzielenie komponentów w aplikacji na osobne fragmenty kodu JavaScript przy minimalnym nakładzie pracy. Następnie możesz zadbać o stany ładowania, łącząc go z komponentem Suspense.

Suspens

Problem z wysyłaniem do użytkowników dużych ładunków JavaScript polega na tym, że wczytywanie strony trwa długo, zwłaszcza na słabszych urządzeniach i przy wolniejszych połączeniach sieciowych. Dlatego dzielenie kodu i leniwe wczytywanie są niezwykle przydatne.

Zawsze jednak występuje niewielkie opóźnienie, z którym użytkownicy muszą się liczyć, gdy komponent podzielony na kod jest pobierany przez sieć. Dlatego ważne jest, aby wyświetlać przydatny stan ładowania. Używanie React.lazy z komponentem Suspense pomaga rozwiązać ten problem.

import React, { lazy, Suspense } from 'react';

const AvatarComponent = lazy(() => import('./AvatarComponent'));

const renderLoader = () => <p>Loading</p>;

const DetailsComponent = () => (
  <Suspense fallback={renderLoader()}>
    <AvatarComponent />
  </Suspense>
)

Komponent Suspense akceptuje komponent fallback, który umożliwia wyświetlanie dowolnego komponentu React jako stanu wczytywania. Poniższy przykład pokazuje, jak to działa. Awatar jest renderowany tylko wtedy, gdy użytkownik kliknie przycisk. Wtedy wysyłane jest żądanie pobrania kodu niezbędnego do zawieszonego AvatarComponent. W tym czasie wyświetlany jest komponent ładowania zastępczego.

W tym przypadku kod, który tworzy AvatarComponent, jest mały, dlatego spinner ładowania wyświetla się tylko przez krótki czas. Większe komponenty mogą się wczytywać znacznie dłużej, zwłaszcza przy słabym połączeniu sieciowym.

Aby lepiej pokazać, jak to działa:

  • Aby wyświetlić podgląd strony, kliknij Wyświetl aplikację, a następnie Pełny ekran pełny ekran.
  • Aby otworzyć Narzędzia dla programistów, naciśnij Ctrl+Shift+J (lub Command+Option+J na Macu).
  • Kliknij kartę Sieć.
  • Kliknij menu Ograniczanie, które domyślnie jest ustawione na Brak ograniczania. Wybierz Szybkie 3G.
  • W aplikacji kliknij przycisk Click Me (Kliknij mnie).

Wskaźnik wczytywania będzie teraz wyświetlany dłużej. Zwróć uwagę, że cały kod, który tworzy AvatarComponent, jest pobierany jako osobny fragment.

Panel sieciowy Narzędzi deweloperskich pokazujący pobieranie jednego pliku chunk.js

Zawieszanie wielu komponentów

Kolejną funkcją Suspense jest możliwość wstrzymania wczytywania wielu komponentów, nawet jeśli wszystkie są wczytywane z opóźnieniem.

Na przykład:

import React, { lazy, Suspense } from 'react';

const AvatarComponent = lazy(() => import('./AvatarComponent'));
const InfoComponent = lazy(() => import('./InfoComponent'));
const MoreInfoComponent = lazy(() => import('./MoreInfoComponent'));

const renderLoader = () => <p>Loading</p>;

const DetailsComponent = () => (
  <Suspense fallback={renderLoader()}>
    <AvatarComponent />
    <InfoComponent />
    <MoreInfoComponent />
  </Suspense>
)

To bardzo przydatny sposób na opóźnienie renderowania wielu komponentów przy jednoczesnym wyświetlaniu tylko jednego stanu wczytywania. Gdy wszystkie komponenty zostaną pobrane, użytkownik zobaczy je wszystkie wyświetlone w tym samym czasie.

Możesz to sprawdzić, korzystając z tego kodu do umieszczania:

Bez tego łatwo napotkać problem stopniowego ładowania, czyli ładowania różnych części interfejsu jedna po drugiej, z osobnym wskaźnikiem ładowania dla każdej z nich. Może to negatywnie wpłynąć na wrażenia użytkowników.

Obsługa błędów wczytywania

Suspense umożliwia wyświetlanie tymczasowego stanu ładowania, gdy w tle są wysyłane żądania sieciowe. Ale co się stanie, jeśli z jakiegoś powodu te żądania sieciowe się nie powiodą? Może to być spowodowane brakiem połączenia z internetem lub próbą leniwego wczytania przez aplikację internetową adresu URL z wersją, który jest nieaktualny i nie jest już dostępny po ponownym wdrożeniu serwera.

React ma standardowy wzorzec do prawidłowego obsługiwania tego typu błędów wczytywania: używanie granicy błędu. Jak opisano w dokumentacji, każdy komponent React może pełnić funkcję granicy błędu, jeśli implementuje jedną z metod cyklu życia static getDerivedStateFromError() lub componentDidCatch() (albo obie).

Aby wykrywać i obsługiwać błędy leniwego wczytywania, możesz umieścić komponent Suspense w komponencie nadrzędnym, który będzie pełnił funkcję granicy błędu. W metodzie render() komponentu granicy błędu możesz renderować elementy podrzędne w niezmienionej postaci, jeśli nie wystąpił błąd, lub renderować niestandardowy komunikat o błędzie, jeśli coś pójdzie nie tak:

import React, { lazy, Suspense } from 'react';

const AvatarComponent = lazy(() => import('./AvatarComponent'));
const InfoComponent = lazy(() => import('./InfoComponent'));
const MoreInfoComponent = lazy(() => import('./MoreInfoComponent'));

const renderLoader = () => <p>Loading</p>;

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = {hasError: false};
  }

  static getDerivedStateFromError(error) {
    return {hasError: true};
  }

  render() {
    if (this.state.hasError) {
      return <p>Loading failed! Please reload.</p>;
    }

    return this.props.children;
  }
}

const DetailsComponent = () => (
  <ErrorBoundary>
    <Suspense fallback={renderLoader()}>
      <AvatarComponent />
      <InfoComponent />
      <MoreInfoComponent />
    </Suspense>
  </ErrorBoundary>
)

Podsumowanie

Jeśli nie wiesz, od czego zacząć dzielenie kodu w aplikacji React, wykonaj te czynności:

  1. Zacznij od poziomu trasy. Trasy to najprostszy sposób identyfikowania punktów w aplikacji, które można podzielić. W dokumentacji Reacta znajdziesz informacje o tym, jak używać Suspense z react-router.
  2. Zidentyfikuj duże komponenty na stronie w Twojej witrynie, które są renderowane tylko w przypadku określonych interakcji użytkownika (np. kliknięcia przycisku). Podział tych komponentów zminimalizuje ładunki JavaScript.
  3. Zastanów się nad podzieleniem wszystkiego, co znajduje się poza ekranem i nie jest kluczowe dla użytkownika.