Разделение кода с помощью динамического импорта в Next.js

Как ускорить работу вашего приложения Next.js с помощью разделения кода и интеллектуальной стратегии загрузки.

Чему вы научитесь?

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

Разделение кода на основе маршрутов и компонентов

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

Хотя разделение кода на основе маршрутов является хорошим вариантом по умолчанию, вы можете дополнительно оптимизировать процесс загрузки с помощью разделения кода на уровне компонентов. Если в вашем приложении есть большие компоненты, хорошей идеей будет разделить их на отдельные части. Таким образом, любые крупные компоненты, которые не являются критически важными или отображаются только при определенных взаимодействиях с пользователем (например, при нажатии кнопки), могут загружаться с отложенной загрузкой.

Next.js поддерживает динамический import() , который позволяет динамически импортировать модули JavaScript (включая компоненты React) и загружать каждый импорт как отдельный фрагмент. Это обеспечивает разделение кода на уровне компонентов и позволяет контролировать загрузку ресурсов, чтобы пользователи загружали только тот код, который им нужен для той части сайта, которую они просматривают. В Next.js эти компоненты по умолчанию визуализируются на стороне сервера (SSR) .

Динамический импорт в действии

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

В первой версии приложения щенок живет в components/Puppy.js . Чтобы отобразить щенка на странице, приложение импортирует компонент Puppy в index.js с помощью статического оператора импорта:

import Puppy from "../components/Puppy";

Чтобы увидеть, как Next.js объединяет приложение, проверьте трассировку сети в DevTools:

  1. Чтобы просмотреть сайт, нажмите «Просмотреть приложение» . Затем нажмите Полноэкранный режим полноэкранный .

  2. Нажмите «Control+Shift+J» (или «Command+Option+J» на Mac), чтобы открыть DevTools.

  3. Откройте вкладку «Сеть» .

  4. Установите флажок Отключить кеш .

  5. Перезагрузите страницу.

Когда вы загружаете страницу, весь необходимый код, включая компонент Puppy.js , включается в index.js :

Вкладка «Сеть DevTools», на которой показаны шесть файлов JavaScript: index.js, app.js, webpack.js, main.js, 0.js и файл dll (библиотека динамической компоновки).

При нажатии кнопки Click me во вкладку Сеть добавляется только запрос на щенячий JPEG:

Вкладка DevTools Network после нажатия кнопки, на которой показаны те же шесть файлов JavaScript и одно изображение.

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

Теперь ознакомьтесь со второй версией приложения, в которой статический импорт заменен динамическим импортом. Next.js включает next/dynamic , что позволяет использовать динамический импорт для любых компонентов в Next:

import Puppy from "../components/Puppy";
import dynamic from "next/dynamic";

// ...

const Puppy = dynamic(import("../components/Puppy"));

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

При первой загрузке приложения загружается только index.js . На этот раз он на 0,5 КБ меньше (он уменьшился с 37,9 КБ до 37,4 КБ), поскольку не включает код компонента Puppy :

Сеть DevTools показывает те же шесть файлов JavaScript, за исключением того, что index.js теперь на 0,5 КБ меньше.

Компонент Puppy теперь находится в отдельном чане 1.js , который загружается только при нажатии кнопки:

Вкладка «Сеть DevTools» после нажатия кнопки, на которой отображается дополнительный файл 1.js и изображение, добавленное в конец списка файлов.

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

Динамический импорт с пользовательским индикатором загрузки

При отложенной загрузке ресурсов рекомендуется предоставлять индикатор загрузки на случай каких-либо задержек. В Next.js это можно сделать, указав дополнительный аргумент функции dynamic() :

const Puppy = dynamic(() => import("../components/Puppy"), {
  loading: () => <p>Loading...</p>
});

Чтобы увидеть индикатор загрузки в действии, смоделируйте медленное сетевое соединение в DevTools:

  1. Чтобы просмотреть сайт, нажмите «Просмотреть приложение» . Затем нажмите Полноэкранный режим полноэкранный .

  2. Нажмите «Control+Shift+J» (или «Command+Option+J» на Mac), чтобы открыть DevTools.

  3. Откройте вкладку Сеть .

  4. Установите флажок Отключить кеш .

  5. В раскрывающемся списке «Регулирование» выберите «Быстрый 3G» .

  6. Нажмите кнопку «Нажми на меня» .

Теперь, когда вы нажимаете кнопку, загрузка компонента занимает некоторое время, и в это время приложение отображает сообщение «Загрузка…».

Темный экран с текстом

Динамический импорт без SSR

Если вам нужно визуализировать компонент только на стороне клиента (например, виджет чата), вы можете сделать это, установив для параметра ssr значение false :

const Puppy = dynamic(() => import("../components/Puppy"), {
  ssr: false,
});

Заключение

Благодаря поддержке динамического импорта Next.js обеспечивает разделение кода на уровне компонентов, что может минимизировать полезную нагрузку JavaScript и сократить время загрузки приложения. По умолчанию все компоненты отображаются на стороне сервера, и при необходимости вы можете отключить эту опцию.