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 nigdy tego nie osiągnąć.

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

import React, { lazy } from 'react';

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

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

Dlaczego to takie przydatne?

Duża aplikacja React zazwyczaj składa się z wielu komponentów, metod użytkowych i bibliotek zewnętrznych. Jeśli nie starasz się wczytywać poszczególnych części aplikacji tylko wtedy, gdy są one potrzebne, wysyłamy użytkownikom pojedynczy, duży pakiet kodu JavaScript zaraz po wczytaniu pierwszej strony. Może to znacznie wpłynąć na wydajność strony.

Funkcja React.lazy zapewnia wbudowaną sposób rozdzielania komponentów aplikacji na osobne fragmenty JavaScriptu, które wymagają minimalnego nakładu pracy. Po połączeniu go z komponentem Suspense możesz zająć się stanami wczytywania.

Suspens

Problem z wysłaniem dużego ładunku JavaScript do użytkowników polega na długim czasie potrzebnym na wczytanie strony, zwłaszcza w przypadku słabszych urządzeń i połączeń sieciowych. Dlatego właśnie podział kodu i leniwe ładowanie są tak przydatne.

Zawsze jednak występują niewielkie opóźnienia, które muszą wystąpić podczas pobierania przez sieć komponentu z podziałem kodu, dlatego ważne jest, aby stan wczytywania był przydatny. 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 dopiero po kliknięciu przycisku i wysyłaniu żądania pobrania kodu niezbędnego do pobrania kodu AvatarComponent. W międzyczasie wyświetlany jest komponent zastępczego wczytywania.

Kod w elemencie AvatarComponent jest w tym przypadku niewielki, dlatego ikona wczytywania wyświetla się tylko przez krótki czas. Wczytywanie większych komponentów może potrwać znacznie dłużej, zwłaszcza w przypadku słabych połączeń sieciowych.

Aby lepiej zademonstrować, jak to działa:

  • Aby wyświetlić podgląd strony, kliknij Wyświetl aplikację, a potem 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 ma domyślnie wartość Bez ograniczania. Wybierz Szybka 3G.
  • Kliknij przycisk Kliknij mnie w aplikacji.

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

Panel sieci DevTools pokazujący pobierany plik chunk.js

Zawieszanie wielu komponentów

Kolejną funkcją Suspense jest możliwość zawieszenia wczytywania wielu komponentów, nawet jeśli są one leniwe.

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>
)

Jest to niezwykle skuteczny sposób na opóźnienie renderowania wielu komponentów w ramach pokazywania tylko jednego stanu wczytywania. Gdy wszystkie komponenty zakończą pobieranie, użytkownik zobaczy je wszystkie w tym samym czasie.

Możesz to zobaczyć, jeśli umieszczone są następujące elementy:

Bez niego łatwo może pojawić się problem z przesuniętym ładowaniem lub wczytywaniem różnych elementów interfejsu jeden po drugim, które mają własny wskaźnik ładowania. Może to sprawić, że wrażenia użytkownika będą bardziej irytujące.

Postępowanie w przypadku błędów wczytywania

Suspense umożliwia wyświetlanie tymczasowego stanu wczytywania, gdy żądania sieciowe są wysyłane. A co, jeśli żądania sieciowe z jakiegoś powodu nie powiodą się? Być może jesteś offline lub Twoja aplikacja internetowa próbuje leniwie wczytywać adres URL z wersją, który jest nieaktualny i nie jest już dostępny po ponownym wdrożeniu serwera.

React ma standardowy wzorzec, który pozwala na płynną obsługę tego typu błędów wczytywania: użycie granicy błędu. Jak opisano w dokumentacji, każdy komponent React może służyć jako granica błędu, jeśli implementuje jedną (lub obie) metody cyklu życia static getDerivedStateFromError() albo componentDidCatch().

Aby wykrywać i radzić sobie z błędami leniwego wczytywania, możesz opakować komponent Suspense komponentami nadrzędnymi, które służą jako granica błędu. W metodzie render() na granicy błędu możesz wyrenderować elementy podrzędne w takiej postaci, w jakiej nie ma błędów, lub wyświetlić 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 masz pewności, gdzie zacząć dzielić kod w aplikacji React, wykonaj te czynności:

  1. Zacznij od poziomu trasy. Trasy to najprostszy sposób identyfikowania punktów aplikacji, które można podzielić. W dokumentach React znajdziesz informacje o tym, jak używać Suspense w połączeniu z react-router.
  2. Zidentyfikuj na stronie w witrynie wszystkie duże komponenty, które renderują się tylko w przypadku określonych interakcji użytkownika (np. kliknięcia przycisku). Rozdzielenie tych komponentów zminimalizuje ładunki JavaScript.
  3. Rozważ podzielenie wszystkiego, co nie jest istotne dla użytkownika i nie jest istotne dla użytkownika.