Пошук уроків, статей та іншого контенту
Завантажуватимете модулі динамічно під час виконання та визначатимете, коли це зменшує початковий бандл.
Звичайний імпорт модуля виконується під час побудови початкового JavaScript-бандла:
import Editor from './Editor';Навіть якщо Editor буде показано лише після натискання кнопки, його код може потрапити до початкового бандла сторінки.
Динамічний імпорт завантажує модуль окремим чанком лише тоді, коли він потрібен:
const Editor = dynamic(() => import('./Editor'));Це корисно для компонентів, які:
не потрібні під час першого відображення сторінки;
мають великий розмір;
використовуються лише в окремому сценарії;
залежать від браузерних API;
відкриваються після дії користувача.
Динамічний імпорт не робить код автоматично швидшим у будь-якій ситуації. Він зменшує початковий бандл, але додає окремий мережевий запит у момент завантаження компонента.
next/dynamic для компонентівNext.js надає функцію dynamic для відкладеного завантаження React-компонентів:
import dynamic from 'next/dynamic';
const Component = dynamic(() => import('./Component'));Функція dynamic приймає функцію, яка повертає import(). Важливо, щоб цей виклик був оголошений на верхньому рівні модуля, а не всередині функції компонента.
Структура файлів:
app/
page.jsx
components/
ReportSection.jsx
ReportPreview.jsxФайл app/page.jsx:
import ReportSection from './components/ReportSection';
export default function Page() {
return (
<main>
<h1>Звіт продажів</h1>
<p>Попередній перегляд звіту завантажиться лише за потреби.</p>
<ReportSection />
</main>
);
}Файл app/components/ReportSection.jsx:
'use client';
import { useState } from 'react';
import dynamic from 'next/dynamic';
const ReportPreview = dynamic(
() => import('./ReportPreview'),
{
loading: () => <p>Завантаження попереднього перегляду...</p>,
},
);
export default function ReportSection() {
const [isOpen, setIsOpen] = useState(false);
return (
<section>
<button type="button" onClick={() => setIsOpen(true)}>
Відкрити попередній перегляд
</button>
{isOpen && <ReportPreview />}
</section>
);
}Файл app/components/ReportPreview.jsx:
'use client';
import { useMemo } from 'react';
export default function ReportPreview() {
const total = useMemo(() => {
const values = [1200, 840, 1560, 920];
return values.reduce((sum, value) => sum + value, 0);
}, []);
return (
<article>
<h2>Попередній перегляд</h2>
<p>Загальна сума: {total} грн</p>
</article>
);
}У цьому прикладі код ReportPreview не потрібен для першого відображення сторінки. Він завантажується, коли isOpen стає true.
На практиці таким компонентом може бути:
редактор тексту;
складний графік;
конструктор форм;
модальне вікно з рідкісним сценарієм;
компонент перегляду файлів.
Поки окремий чанк завантажується, користувач може побачити компонент, переданий у властивості loading:
const Chart = dynamic(
() => import('./Chart'),
{
loading: () => <p>Завантаження графіка...</p>,
},
);Це краще, ніж залишати порожнє місце або створювати враження, що кнопка не спрацювала.
Компонент завантаження має бути коротким і відповідати приблизному розміру майбутнього вмісту. Для складних інтерфейсів можна показувати спрощений заповнювач.
Якщо файл експортує компонент не за замовчуванням, використовуйте then:
import dynamic from 'next/dynamic';
const SettingsPanel = dynamic(
() => import('./SettingsPanel').then((module) => module.SettingsPanel),
);Файл SettingsPanel.jsx:
'use client';
export function SettingsPanel() {
return <section>Налаштування профілю</section>;
}Для експорту default додаткове перетворення не потрібне:
const SettingsPanel = dynamic(() => import('./SettingsPanel'));За замовчуванням Next.js може відрендерити динамічно імпортований компонент на сервері, а потім передати результат браузеру.
Якщо компонент використовує тільки браузерні API, наприклад window, document або localStorage, його можна завантажувати без серверного відображення:
'use client';
import dynamic from 'next/dynamic';
const BrowserOnlyWidget = dynamic(
() => import('./BrowserOnlyWidget'),
{
ssr: false,
},
);
export default function PageControls() {
return <BrowserOnlyWidget />;
}ssr: false означає, що компонент не буде рендеритися на сервері.
Ця опція доступна для Client Components. У Next.js App Router не можна використовувати ssr: false безпосередньо в Server Component. У такому разі динамічний імпорт потрібно оголосити у Client Component, як у прикладі вище.
Використовуйте ssr: false лише тоді, коли це справді потрібно. Вимкнення серверного відображення може погіршити перше відображення та доступність вмісту.
next/dynamic призначений для React-компонентів. Для функцій, утиліт та інших JavaScript-модулів використовуйте стандартний import().
Наприклад, модуль app/lib/buildSummary.js:
export function buildSummary(items) {
return items
.map((item) => `${item.name}: ${item.quantity}`)
.join('\n');
}Client Component, який імпортує його тільки після натискання кнопки:
'use client';
import { useState } from 'react';
export default function SummaryButton() {
const [summary, setSummary] = useState('');
async function handleBuildSummary() {
const { buildSummary } = await import('../lib/buildSummary');
const result = buildSummary([
{ name: 'Книги', quantity: 3 },
{ name: 'Зошити', quantity: 5 },
]);
setSummary(result);
}
return (
<section>
<button type="button" onClick={handleBuildSummary}>
Побудувати підсумок
</button>
{summary && <pre>{summary}</pre>}
</section>
);
}import() повертає Promise. Після його виконання можна отримати експорт модуля та викликати потрібну функцію.
Такий підхід підходить для операцій, які виконуються рідко, наприклад для:
експорту даних;
побудови складного звіту;
оброблення файлу;
форматування великого набору даних.
Збірник Next.js має заздалегідь знати, які модулі можуть бути завантажені. Тому динамічний шлях не повинен бути довільним значенням користувача:
import(`./widgets/${widgetName}`);Такий код може не працювати очікуваним чином або призвести до створення зайвої кількості чанків.
Краще явно описувати дозволені варіанти:
const widgets = {
chart: () => import('./Chart'),
report: () => import('./Report'),
};
const loadWidget = widgets[type];
if (!loadWidget) {
throw new Error('Невідомий тип компонента');
}
const module = await loadWidget();У цьому випадку список можливих модулів відомий під час побудови застосунку.
Динамічний імпорт зазвичай виправданий, якщо компонент:
Не потрібен у першому viewport.
Відкривається після дії користувача.
Містить великий обсяг коду або залежності.
Використовується лише на окремих сторінках.
Може бути безпечно завантажений після першого відображення.
Не варто динамічно імпортувати кожен маленький компонент. Для невеликого компонента додатковий запит може коштувати дорожче, ніж його залишення у початковому бандлі.
Оцінюйте:
розмір початкового JavaScript;
розмір окремого чанка;
частоту використання компонента;
час завантаження компонента після дії користувача;
вплив на перше відображення сторінки.
Перевірити результат можна, зібравши застосунок у production-режимі:
npm run buildПід час аналізу результату звертайте увагу, чи справді код необов’язкового компонента винесено в окремий чанк і чи зменшився початковий бандл потрібної сторінки.
Неправильно:
function Page() {
const Component = dynamic(() => import('./Component'));
return <Component />;
}Оголошуйте динамічний компонент поза функцією компонента:
const Component = dynamic(() => import('./Component'));
function Page() {
return <Component />;
}next/dynamic для звичайної функціїnext/dynamic очікує React-компонент. Для утиліти або функції використовуйте стандартний import():
const { calculate } = await import('./calculate');ssr: falseЯкщо компонент не використовує браузерні API, не потрібно вимикати серверне відображення. Це може погіршити початкове відображення сторінки.
Окремий чанк може завантажуватися із затримкою. Передбачайте loading для next/dynamic або стан завантаження для звичайного import().
Якщо компонент завжди відображається на першому екрані, його динамічне завантаження може лише додати затримку. Спочатку визначте, чи справді цей код можна відкласти.
next/dynamic використовується для відкладеного завантаження React-компонентів.
Звичайний import() використовується для функцій та інших JavaScript-модулів.
Динамічний імпорт може зменшити початковий бандл і прискорити перше завантаження.
Найбільшу користь він дає для великих або рідко використовуваних компонентів.
Властивість loading показує індикатор під час завантаження чанка.
ssr: false слід використовувати лише для компонентів, які не можуть працювати під час серверного рендерингу.
Ефект потрібно перевіряти в production-збірці, а не оцінювати лише за структурою коду.