Podział kodu za pomocą React.lazy i Susense

Nie musisz wysyłać użytkownikom więcej kodu, niż jest to konieczne, więc podziel pakiety, aby do tego nie doszło.

Metoda React.lazy ułatwia dzielenie kodu aplikacji React na poziomie komponentu za pomocą dynamicznego importowania.

import React, { lazy } from 'react';

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

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

Dlaczego ten przewodnik jest przydatny?

Duża aplikacja React zwykle składa się z wielu komponentów, metod pomocniczych i bibliotek innych firm. Jeśli nie spróbujesz wczytywać różnych części aplikacji tylko wtedy, gdy są potrzebne, użytkownicy otrzymają jeden duży pakiet JavaScriptu, gdy tylko wczytają pierwszą stronę. Może to znacząco wpłynąć na wydajność strony.

Funkcja React.lazy zapewnia wbudowany sposób rozdzielania komponentów w aplikacji na osobne fragmenty kodu JavaScript przy minimalnym nakładzie pracy. Możesz wtedy zająć się stanami wczytywania, łącząc go z komponentem Suspense.

Suspens

Problem z wysyłaniem użytkownikom dużego ładunku JavaScriptu polega na tym, że wczytywanie strony trwa długo, zwłaszcza na słabszych urządzeniach i przy wolniejszych połączeniach sieciowych. Dlatego podział kodu i leniwe ładowanie są niezwykle przydatne.

Jednak podczas pobierania komponentu z podziałem kodu przez sieć zawsze występuje niewielkie opóźnienie, dlatego ważne jest, aby wyświetlać przydatny stan wczytywania. Użycie 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>
)

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 po kliknięciu przycisku, gdy wysyłane jest żądanie pobrania kodu niezbędnego do zawieszonego elementu AvatarComponent. W tym czasie wyświetlany jest komponent zastępczy.

W tym przypadku kod, który tworzy AvatarComponent, jest niewielki, dlatego ikona ładowania wyświetla się tylko przez krótki czas. Wczytywanie większych komponentów może potrwać znacznie dłużej, zwłaszcza przy słabym połączeniu sieciowym.

Aby lepiej pokazać, jak to działa:

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

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

Panel sieci w Narzędziach deweloperskich pokazujący pobieranie jednego pliku chunk.js

Zawieszanie wielu komponentów

Kolejną funkcją Suspense jest możliwość zawieszenia ładowania wielu komponentów, nawet jeśli wszystkie są ładowane leniwie.

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 ładowania. Gdy wszystkie komponenty zostaną pobrane, użytkownik zobaczy je wszystkie jednocześnie.

Możesz to zobaczyć na tym przykładzie:

Bez tego łatwo o problem ze stopniowym wczytywaniem, czyli sytuacją, w której różne części interfejsu wczytują się jedna po drugiej, a każda ma własny wskaźnik wczytywania. 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 żądania sieciowe są wysyłane w tle. Co jednak, jeśli te żądania sieciowe z jakiegoś powodu się nie powiodą? Być może jesteś offline lub Twoja aplikacja internetowa próbuje leniwie załadować adres URL z wersją, który jest nieaktualny i nie jest już dostępny po ponownym wdrożeniu serwera.

React ma standardowy wzorzec do obsługi tego typu błędów wczytywania: używanie granicy błędu. Jak opisano w dokumentacji, dowolny komponent React może służyć jako granica błędu, jeśli implementuje jedną z metod cyklu życia static getDerivedStateFromError() lub componentDidCatch() (albo obie).

Aby wykrywać i obsługiwać błędy leniwego ładowania, możesz opakować komponent Suspense w komponent nadrzędny, który służy jako granica błędu. W metodzie render() granicy błędu możesz renderować elementy podrzędne w niezmienionej postaci, jeśli nie ma błędu, 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ąć stosowanie podziału kodu w aplikacji React, wykonaj te czynności:

  1. Zacznij od poziomu trasy. Trasy to najprostszy sposób na zidentyfikowanie punktów aplikacji, które można podzielić. Dokumentacja React pokazuje, jak można używać Suspense razem z react-router.
  2. Zidentyfikuj na stronie w witrynie duże komponenty, które renderują się tylko podczas określonych interakcji użytkownika (np. kliknięcia przycisku). Podział tych komponentów zminimalizuje ładunki JavaScriptu.
  3. Rozważ podzielenie innych elementów, które znajdują się poza ekranem i nie są kluczowe dla użytkownika.