Ленивая загрузка (Lazy Loading)
Ленивая загрузка в Next.js помогает улучшить начальную производительность загрузки приложения за счёт уменьшения количества JavaScript, необходимого для рендеринга маршрута.
Она позволяет отложить загрузку Клиентских компонентов (Client Components) и импортируемых библиотек, включая их в клиентский бандл только тогда, когда они действительно нужны. Например, вы можете отложить загрузку модального окна до момента, когда пользователь кликнет для его открытия.
В Next.js есть два способа реализации ленивой загрузки:
- Использование Динамических импортов с
next/dynamic
- Использование
React.lazy()
с Suspense
По умолчанию Серверные компоненты (Server Components) автоматически подвергаются разделению кода (code splitting), и вы можете использовать потоковую передачу (streaming) для постепенной отправки частей интерфейса с сервера на клиент. Ленивая загрузка применяется к Клиентским компонентам.
next/dynamic
next/dynamic
представляет собой комбинацию React.lazy()
и Suspense. Он работает одинаково в директориях app
и pages
, что позволяет постепенно мигрировать.
Примеры
При использовании next/dynamic
компонент заголовка не будет включён в начальный JavaScript-бандл страницы. Сначала страница отобразит fallback
из Suspense, а затем компонент Header
, когда граница Suspense будет разрешена.
import dynamic from 'next/dynamic'
const DynamicHeader = dynamic(() => import('../components/header'), {
loading: () => <p>Загрузка...</p>,
})
export default function Home() {
return <DynamicHeader />
}
Важно знать: В
import('path/to/component')
путь должен быть явно указан. Нельзя использовать шаблонные строки или переменные. Кроме того,import()
должен находиться внутри вызоваdynamic()
, чтобы Next.js мог сопоставить вебпак-бандлы/идентификаторы модулей с конкретным вызовомdynamic()
и предзагрузить их перед рендерингом.dynamic()
нельзя использовать внутри React-рендеринга, так как он должен быть помечен на верхнем уровне модуля для работы предзагрузки, аналогичноReact.lazy
.
С именованными экспортами
Для динамического импорта именованного экспорта вы можете вернуть его из Promise, возвращаемого import()
:
export function Hello() {
return <p>Привет!</p>
}
// pages/index.js
import dynamic from 'next/dynamic'
const DynamicComponent = dynamic(() =>
import('../components/hello').then((mod) => mod.Hello)
)
Без SSR
Для динамической загрузки компонента на стороне клиента можно использовать опцию ssr
для отключения серверного рендеринга. Это полезно, если внешняя зависимость или компонент полагается на браузерные API, такие как window
.
import dynamic from 'next/dynamic'
const DynamicHeader = dynamic(() => import('../components/header'), {
ssr: false,
})
С внешними библиотеками
В этом примере используется внешняя библиотека fuse.js
для нечёткого поиска. Модуль загружается в браузере только после ввода пользователем поискового запроса.
import { useState } from 'react'
const names = ['Tim', 'Joe', 'Bel', 'Lee']
export default function Page() {
const [results, setResults] = useState()
return (
<div>
<input
type="text"
placeholder="Поиск"
onChange={async (e) => {
const { value } = e.currentTarget
// Динамическая загрузка fuse.js
const Fuse = (await import('fuse.js')).default
const fuse = new Fuse(names)
setResults(fuse.search(value))
}}
/>
<pre>Результаты: {JSON.stringify(results, null, 2)}</pre>
</div>
)
}