Ленивая загрузка клиентских компонентов и библиотек
Ленивая загрузка в Next.js помогает улучшить начальную производительность загрузки приложения, уменьшая количество JavaScript, необходимого для рендеринга маршрута.
Она позволяет отложить загрузку клиентских компонентов (Client Components) и импортируемых библиотек, включая их в клиентский бандл только тогда, когда они действительно нужны. Например, вы можете отложить загрузку модального окна до момента, когда пользователь кликнет для его открытия.
В Next.js есть два способа реализации ленивой загрузки:
- Использование динамических импортов с
next/dynamic
- Использование
React.lazy()
с Suspense
По умолчанию серверные компоненты (Server Components) автоматически подвергаются разделению кода (code splitting), и вы можете использовать стриминг для постепенной отправки частей UI с сервера на клиент. Ленивая загрузка применяется к клиентским компонентам.
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 мог сопоставить бандлы webpack / идентификаторы модулей с конкретным вызовом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
.
'use client'
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>
)
}