Skip to content
Обучение Измерение Блог Case studies О сайте
The PWA community is coming together for #PWASummit22. Have a great story about developing a web app? Submit your talk today
Содержание
  • Какая от этого польза?
  • Suspense
  • Приостановка нескольких компонентов
  • Обработка сбоев загрузки
  • Заключение

Разделение кода с помощью React.lazy и Suspense

Чем меньше вы отправляете пользователям кода, тем лучше, поэтому разделяйте пакеты, чтобы не отправлять ненужный код!

Apr 29, 2019
Available in: Español, 한국어, Português, 中文, English
Appears in: React
Houssein Djirdeh
Houssein Djirdeh
TwitterGitHubGlitchHomepage
Jeff Posnick
Jeff Posnick
TwitterGitHubGlitchHomepage
Содержание
  • Какая от этого польза?
  • Suspense
  • Приостановка нескольких компонентов
  • Обработка сбоев загрузки
  • Заключение
Если вы еще не разобрались в том, для чего нужно разделение кода, сначала ознакомьтесь со статьей Сокращение полезной нагрузки JavaScript за счет разделения кода.

Метод React.lazy упрощает разделение кода для приложения React на уровне компонентов с помощью динамического импорта.

import React, { lazy } from 'react';

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

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

Какая от этого польза? #

Большое приложение React обычно состоит из множества компонентов, служебных методов и сторонних библиотек. Если не пытаться загружать различные части приложения только по необходимости, то при загрузке первой страницы пользователям будет отправлен один большой пакет JavaScript, что может существенно повлиять на скорость работы страницы.

Функция React.lazy — это встроенное средство разделения компонентов в приложении на отдельные фрагменты кода JavaScript с минимальными усилиями. После разделения можно будет настроить состояния загрузки — используя компонент Suspense.

Suspense #

При отправке пользователям большого объема полезной нагрузки JavaScript на загрузку страницы требуется много времени, особенно на слабых устройствах и при медленном подключении. В таких случаях на помощь приходит разделение кода и отложенная загрузка.

Однако при получении компонента с разделением кода по сети всегда будет небольшая задержка, поэтому важно отображать понятное для пользователей состояние загрузки. В решении этой задачи помогает React.lazy с компонентом Suspense.

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

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

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

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

Suspense принимает компонент fallback, который позволяет отображать любой компонент React как состояние загрузки. В следующем примере показано, как это работает. Аватар отрисовывается только при нажатии кнопки: при этом делается запрос на получение кода, необходимого для приостановленного AvatarComponent, а отображается компонент загрузки fallback.

В этом примере код AvatarComponent небольшой, поэтому счетчик загрузки отображается недолго. На загрузку более объемных компонентов может потребоваться гораздо больше времени, особенно при медленном подключении.

Разберемся подробнее, как это работает:

  • To preview the site, press View App. Then press Fullscreen fullscreen.
  • Откройте DevTools, нажав Control+Shift+J (или Command+Option+J, если у вас Mac).
  • Перейдите на вкладку Сеть.
  • Откройте список Throttling (Ограничение) — по умолчанию там Без ограничения. Выберите Fast 3G (3G (высокая скорость)).
  • Нажмите кнопку Click Me в приложении.

Индикатор загрузки будет отображаться дольше. Обратите внимание, что весь код AvatarComponent передается как отдельный фрагмент.

Панель «Сеть» в DevTools. Загружается файл chunk.js
Пока что React не поддерживает Suspense, если компоненты отрисовываются на сервере — в этом случае можно попробовать другую библиотеку, например loadable-components, которую рекомендуют в документации React.

Приостановка нескольких компонентов #

Еще одна возможность Suspense — приостановка загрузки нескольких компонентов, даже в случае отложенной загрузки.

Например:

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

Это позволяет удобным образом откладывать отрисовку нескольких компонентов, показывая только одно состояние загрузки. Как только все компоненты будут получены, пользователь увидит их все одновременно.

Увидеть это можно на следующей вставке:

Индикатор загрузки исчезает слишком быстро? Попробуйте снова ограничить скорость подключения в DevTools.

Если не использовать такой подход, можно столкнуться с проблемой несинхронной загрузки: разные части интерфейса будут появляться друг за другом, и у каждого будет собственный индикатор загрузки — пользователя это наверняка будет раздражать.

Suspense уже позволяет разделять компоненты и сокращать размер пакетов, но команда React продолжает работать над другими функциями, которые дадут еще больше возможностей. Подробнее — в дорожной карте React 16.x.

Обработка сбоев загрузки #

Suspense позволяет отображать временное состояние загрузки, пока выполняются сетевые запросы. Но что, если эти сетевые запросы по какой-либо причине не срабатывают? Возможно, пользователь офлайн, или веб-приложение пытается отложить загрузку устаревшего версионированного URL-адреса, который после повторного развертывания сервера перестал быть доступен.

Для этого в React есть стандартный шаблон работы с такими проблемами загрузки: использование границы ошибок. Как описано в документации, любой границей ошибок может быть любой компонент React, если он реализует один или оба метода жизненного цикла: static getDerivedStateFromError() или componentDidCatch().

Для обнаружения и обработки сбоев при отложенной загрузке можно обернуть компонент Suspense родительскими компонентами, которые будут служить границей ошибок. В методе границы ошибок render() в случае нормальной работы дочерние элементы можно отрисовывать как есть, при сбое отображать заданное сообщение об ошибке:

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

Заключение #

Если вы не знаете, как подступиться к разделению кода в приложении React, действуйте следующим образом:

  1. Начните с уровня маршрута. Маршруты — самый простой способ определить точки, в которых приложение можно разделить. В документации React показано, как использовать Suspense в сочетании с react-router.
  2. Найдите на странице сайта большие компоненты, которые отрисовываются только при определенных действиях пользователя (например, при нажатии кнопки). Отделив их, вы уменьшите размера полезных нагрузок JavaScript.
  3. Попробуйте разделить всё, что находится «за кадром» и не критично для пользователя.
Последнее обновление: Apr 29, 2019 — Улучшить статью
Return to all articles
Поделиться
подписаться

Contribute

  • Сообщить об ошибке
  • Просмотреть исходный код

Дополнительная информация

  • developer.chrome.com
  • Новости Chrome
  • Web Fundamentals
  • Разборы конкретных случаев
  • Подкасты
  • Шоу

Соцсети

  • Twitter
  • YouTube
  • Google Developers
  • Chrome
  • Firebase
  • Google Cloud Platform
  • Все продукты
  • Условия и конфиденциальность
  • Правила сообщества

Except as otherwise noted, the content of this page is licensed under the Creative Commons Attribution 4.0 License, and code samples are licensed under the Apache 2.0 License. For details, see the Google Developers Site Policies.