Главная » Статьи » Как использовать React.lazy и Suspense для отложенной загрузки компонентов

Как использовать React.lazy и Suspense для отложенной загрузки компонентов

Как использовать React.lazy и Suspense для отложенной загрузки компонентов

От автора: React 16.6 вывел разделение кода на новый уровень. Теперь вы можете загружать компоненты, когда это действительно необходимо, без установки дополнительных библиотек.

Что такое разделение кода и отложенная загрузка?

Webpack определяет разделение кода как: «Метод разделения вашего кода на различные пакеты, которые затем могут быть загружены по требованию или параллельно».

Другой способ сказать: «загрузка по требованию или параллельно» — это отложенная загрузка. Противоположностью отложенной загрузке являются прямая загрузка. При ней все загружается независимо от того, используете вы это или нет.

Для чего использовать разделение кода и отложенную загрузку?

Иногда нам приходится вводить большой фрагмент кода для реализации некоторых функций. Этот код может импортировать стороннюю зависимость или записывать ее самостоятельно. Этот код влияет на размер основного пакета.

Загрузка нескольких мегабайт — это пустяки для сегодняшней интернет-скорости. Но нам по-прежнему приходится думать о пользователях с медленным подключением к Интернету или пользователях мобильных устройств.

Как это было до React 16.6?

Вероятно, самая популярная библиотека React для отложенной загрузки компонентов — react-loadable. Важно, что responsejs.org по-прежнему рекомендует react-loadable, если ваше приложение визуализируется на сервере. react-loadable на самом деле очень похож на новый подход React. Я покажу это в следующем примере.

Что-нибудь нужно для настройки?

Посмотрим, что reactjs.org говорит по этому поводу: «Если вы используете Create React App, Next.js , Gatsby или аналогичный инструмент, у вас будет из коробки установлен Webpack, чтобы упаковать ваше приложение. Если вы этого не сделаете, вам нужно будет настроить упаковку самому. Например, см. Руководства по установке и началу работы в документации Webpack.»

Хорошо, нам требуется Webpack, который обрабатывает динамический импорт пакетов. Приведенная ниже демо-версия сгенерирована Create React App. И в этом случае Webpack уже настроен, и мы готовы к работе.

Демо-версия

Для этой демо-версии мы будем использовать react-pdf. react-pdf — это удивительная библиотека, используемая для создания PDF-файлов в браузере, на мобильных устройствах и на сервере. Мы могли бы создать PDF-файл на сервере, но если мы сделаем это на стороне клиента, размер пакетов будет меньше.

Я использую расширение для Visual Studio Code Import cost, чтобы увидеть размеры используемых библиотек. Скажем, нам нужно, чтобы, когда пользователь нажимает кнопку, генерировался PDF-файл.

Теперь это простая форма с одним вариантом использования. Попытайтесь представить себе огромное веб-приложение, в котором это является небольшой частью функционала. Возможно, эта функция не очень часто используется пользователями.
Давайте представим себе эту ситуацию. Генерация PDF не используется очень часто, и нет смысла загружать весь код для каждого запроса страницы.

Я попытаюсь показать, как мы можем разработать решение с отложенной загрузкой и без нее.

Примеры прямой и отложенной загрузки

В обоих случаях мы будем использовать один компонент, который импортирует зависимости react-pdf и создает простой PDF-документ.

import React from "react";
import { PDFViewer, Document, Page, Text, View } from "@react-pdf/renderer";
import pdfstyles from "./pdfStyles"; // Create Document Component
const PDFPreview = ({ title }) => ( <PDFViewer className="viewer" style={pdfstyles.viewer}> <Document> <Page size="A4" style={pdfstyles.page}> <View style={pdfstyles.section}> <Text style={pdfstyles.title}>{title}</Text> <Text>This is a text in a generated PDF file.</Text> </View> </Page> </Document> </PDFViewer>
); export default PDFPreview;

Ничего захватывающего здесь не происходит. Мы импортируем PDFViewer, Document, Page, Text, Viewс из react-pdf. Все они используются в методе render компонента PDFPreview.

PDFPreview получает только одно свойство — title. Как следует из названия, оно используется, как заголовок создаваемого PDF-файла.

Прямая загрузка

Давайте сначала рассмотрим, как выглядит родительский компонент без отложенной загрузки:

import React, { Component } from "react";
import PDFPreview from "./PDFPreview"; class App extends Component { state = { name: "", showPDFPreview: false }; handleClick = () => this.setState({ showPDFPreview: true }); handleNameChange = event => this.setState({ name: event.target.value }); render() { const greeting = `Hello ${this.state.name}`; return ( <div className="App"> <input placeholder="Enter your name" type="text" onChange={this.handleNameChange} /> <button onClick={this.handleClick}>Generate PDF</button> {this.state.showPDFPreview && <PDFPreview title={greeting} />} </div> ); }
} export default App;

Давайте рассмотрим подробнее код. В строке 2 мы импортируем компонент PDFPreview. В строке 6 мы инициализируем состояние со значениями по умолчанию. name — это поле, используемое как заголовок в файле PDF, а PDFPreview — логическое значение, которое задает отображение или скрытие PDFPreview.

Теперь давайте перейдем к методу render и проверим, что будет отображаться. В строке 19 и 25 мы отображаем введенные данные и кнопку. Когда пользователь вводит данные, name в состоянии изменяется.

Затем, когда пользователь нажимает кнопку «Generate PDF», для showPDFPreview устанавливается значение true. Компонент повторно отображает и показывает компонент PDFPreview.

Несмотря на то, что мы используем PDFPreview только при клике пользователя, весь связанный с ним код входит в комплект приложения:

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

Отложенная загрузка

import React, { Component, Suspense } from "react";
const LazyPDFDocument = React.lazy(() => import("./PDFPreview")); class App extends Component { state = { name: "", showPDFPreview: false }; handleClick = () => this.setState({ showPDFPreview: true }); handleNameChange = event => this.setState({ name: event.target.value }); render() { const greeting = `Hello ${this.state.name}`; return ( <div className="App"> <input placeholder="Enter your name" type="text" onChange={this.handleNameChange} /> <button onClick={this.handleClick}>Generate PDF</button> {this.state.showPDFPreview && ( <Suspense fallback={<div>Loading...</div>}> <LazyPDFDocument title={greeting} /> </Suspense> )} </div> ); }
} export default App;

Мы внесли только несколько небольших изменений, давайте их рассмотрим. Строка 2 заменяется на:

const LazyPDFDocument = React.lazy(() => import("./PDFPreview"));

Давайте посмотрим, что в документации React говорится о React.lazy: React.lazy принимает функцию, которая должна вызывать динамический import(). Он должен возвращать значение Promise, которое преобразуется в модуль с экспортом default, содержащим компонент React.

В строке 27 мы используем Suspense, который должен быть родительским компонентом для отложено загружаемого компонента. Когда для showPDFPreview установлено значение true, LazyPDFDocument начинает загружаться.

Пока дочерний компонент не будет разрешен, Suspense отображается все, что предоставляется для свойства fallback. Конечный результат выглядит следующим образом:

Мы можем видеть, что размер 0.chunk.js значительно меньше, чем раньше, а 4.chunk.js и 3.chunk.js загружаются при нажатии кнопки.

Заключение

Каждый раз, когда мы вводим в проект новую зависимость, мы обязаны оценить его влияние на основной пакет. Затем мы должны спросить себя, будет ли этот функционал использоваться часто, и можем ли мы загружать его по требованию, не жертвуя опытом пользователя. Если ответ «да», то React.Lazy и Suspense действительно помогут нам справиться с этой задачей.

Спасибо за внимание! Пожалуйста, поделитесь ссылкой на этот пост и оставьте отзыв.

Автор: Boris Sever

Источник: https://medium.freecodecamp.org/

Редакция: Команда webformyself.