Пошук уроків, статей та іншого контенту
Застосуєте поділ коду, щоб завантажувати JavaScript лише для потрібних маршрутів і компонентів.
Поділ коду — це поділ JavaScript-застосунку на менші частини, які завантажуються лише тоді, коли вони потрібні.
Без поділу коду браузер може отримати великий JavaScript-файл одразу, навіть якщо користувач відкрив лише одну сторінку. Це збільшує:
час першого завантаження;
обсяг переданих даних;
час виконання JavaScript у браузері.
Next.js автоматично ділить код за маршрутами. Наприклад, код сторінки /reports не потрібно завантажувати під час відкриття /.
Однак у межах одного маршруту можуть бути великі або рідко використовувані компоненти:
редактор;
графік;
модальне вікно;
компонент із браузерною бібліотекою;
складний віджет, який відкривається після дії користувача.
Для таких компонентів можна створити окремий chunk за допомогою next/dynamic.
У App Router кожен маршрут має власний набір залежностей. Наприклад:
app/
├── page.tsx
├── reports/
│ └── page.tsx
└── settings/
└── page.tsxКод, який використовується лише в app/reports/page.tsx, не повинен потрапляти до JavaScript, необхідного для головної сторінки.
Next.js аналізує імпорти під час збирання застосунку та створює окремі частини коду для маршрутів. Під час переходу на інший маршрут Next.js завантажує необхідні дані та JavaScript.
У production-режимі частина маршрутів може бути попередньо завантажена під час наведення курсору або відповідно до стратегії prefetching. Проте це не означає, що весь код застосунку завантажується одразу.
Для додаткового поділу коду використовується dynamic:
import dynamic from 'next/dynamic';
const Component = dynamic(() => import('./Component'));Функція import() у цьому прикладі вказує Next.js, що компонент потрібно винести в окремий chunk.
Компонент завантажиться тоді, коли він буде потрібен на сторінці. До завершення завантаження можна показати запасний інтерфейс:
const Component = dynamic(() => import('./Component'), {
loading: () => <p>Завантаження...</p>,
});Шлях імпорту має бути статичним:
dynamic(() => import('./Chart'));Не слід будувати його під час виконання:
// Не рекомендовано для next/dynamic
dynamic(() => import(`./widgets/${widgetName}`));Статичний шлях дозволяє Next.js визначити залежність під час збирання.
Уявімо сторінку звітів. Вона містить кнопку, після натискання якої відкривається складний редактор. Немає потреби завантажувати редактор разом зі сторінкою, якщо користувач ним не користується.
app/reports/page.tsximport ReportTools from './ReportTools';
export default function ReportsPage() {
return (
<main>
<h1>Звіти</h1>
<p>Оберіть дію для роботи зі звітом.</p>
<ReportTools />
</main>
);
}Сторінка є Server Component за замовчуванням. Інтерактивність винесемо в окремий Client Component.
app/reports/ReportTools.tsx'use client';
import { useState } from 'react';
import dynamic from 'next/dynamic';
const ReportEditor = dynamic(() => import('./ReportEditor'), {
loading: () => <p>Завантаження редактора...</p>,
ssr: false,
});
export default function ReportTools() {
const [isEditorOpen, setIsEditorOpen] = useState(false);
return (
<section>
<button
type="button"
onClick={() => setIsEditorOpen(true)}
>
Відкрити редактор
</button>
{isEditorOpen && <ReportEditor />}
</section>
);
}app/reports/ReportEditor.tsx'use client';
import { useEffect, useState } from 'react';
export default function ReportEditor() {
const [windowWidth, setWindowWidth] = useState<number | null>(null);
useEffect(() => {
const updateWindowWidth = () => {
setWindowWidth(window.innerWidth);
};
updateWindowWidth();
window.addEventListener('resize', updateWindowWidth);
return () => {
window.removeEventListener('resize', updateWindowWidth);
};
}, []);
return (
<div>
<h2>Редактор звіту</h2>
<textarea
rows={8}
cols={50}
placeholder="Введіть текст звіту"
/>
<p>
Ширина вікна: {windowWidth ?? 'визначається...'} px
</p>
</div>
);
}У цьому прикладі:
/reports завантажує код маршруту.
ReportTools завантажується як Client Component, оскільки використовує useState.
ReportEditor винесений в окремий chunk.
Код ReportEditor завантажується лише після того, як isEditorOpen стане true.
Поки chunk завантажується, відображається Завантаження редактора....
ssrЗа замовчуванням Next.js може відрендерити динамічно імпортований компонент на сервері:
const ReportEditor = dynamic(() => import('./ReportEditor'));Це підходить для компонентів, які не залежать безпосередньо від браузерних API.
Параметр ssr: false вимикає серверний рендеринг цього компонента:
const ReportEditor = dynamic(() => import('./ReportEditor'), {
ssr: false,
});Це корисно, коли компонент використовує API, доступні лише в браузері:
window;
document;
localStorage;
бібліотеки, які звертаються до браузера під час імпорту.
ssr: false можна використовувати в Client Component. У прикладі таким компонентом є ReportTools, який містить директиву:
'use client';Не потрібно використовувати ssr: false для кожного динамічного компонента. Якщо компонент сумісний із серверним рендерингом, краще залишити SSR увімкненим:
const HelpPanel = dynamic(() => import('./HelpPanel'), {
loading: () => <p>Завантаження довідки...</p>,
});Динамічний імпорт доцільний, якщо компонент:
має великий обсяг JavaScript;
потрібен лише на окремому маршруті;
відкривається після натискання кнопки;
знаходиться всередині закритої модальної панелі;
використовується лише для рідкісного сценарію;
залежить від браузерного API;
містить важку клієнтську логіку.
Наприклад, немає сенсу завантажувати редактор діаграм для кожного відвідувача сторінки, якщо він відкривається лише після натискання кнопки.
Водночас не слід динамічно імпортувати кожен маленький компонент. Надмірний поділ може збільшити кількість мережевих запитів і ускладнити застосунок.
У Next.js Client Components надсилають JavaScript до браузера, оскільки вони можуть використовувати:
стан;
обробники подій;
ефекти;
браузерні API.
Server Components виконуються на сервері й зазвичай не потребують окремого JavaScript у браузері для своєї розмітки.
Тому ефективний поділ коду часто виглядає так:
Основна сторінка залишається Server Component.
Інтерактивна частина виділяється в невеликий Client Component.
Великий або рідко потрібний Client Component завантажується через dynamic.
Це допомагає не перетворювати всю сторінку на Client Component.
Під час завантаження динамічного компонента користувач має бачити зрозумілий стан інтерфейсу:
const Statistics = dynamic(() => import('./Statistics'), {
loading: () => (
<div aria-live="polite">
Завантаження статистики...
</div>
),
});Стан завантаження повинен мати приблизно такий самий розмір, як і майбутній компонент. Це допомагає зменшити стрибки вмісту на сторінці.
Винесення маленьких компонентів в окремі chunks не завжди покращує продуктивність. Додаткові chunks також потрібно завантажити й виконати.
Динамічно завантажуйте компоненти, які справді:
великі;
рідко використовуються;
не потрібні під час першого відображення.
ssr: false без потребиЯкщо компонент не використовує браузерні API, не потрібно вимикати серверний рендеринг. Це може погіршити перше відображення компонента.
dynamic у серверному компоненті з ssr: falseУ App Router параметр ssr: false потрібно використовувати в Client Component. Якщо сторінка є Server Component, винесіть динамічний імпорт у файл із директивою 'use client'.
Такий код виконається і на сервері, якщо компонент рендериться сервером:
const width = window.innerWidth;Для браузерного коду використовуйте useEffect або завантажуйте компонент із ssr: false:
'use client';
import { useEffect, useState } from 'react';
export default function BrowserInfo() {
const [width, setWidth] = useState<number | null>(null);
useEffect(() => {
setWidth(window.innerWidth);
}, []);
return <p>Ширина: {width ?? 'визначається...'}</p>;
}Динамічний компонент може ще не бути доступним у момент першого рендерингу. Завжди передбачайте стан завантаження або інший прийнятний fallback.
Next.js має бачити конкретний шлях до модуля під час збирання:
dynamic(() => import('./ReportEditor'));Змінні шляхи ускладнюють визначення chunks і не є правильним способом використання next/dynamic.
Next.js автоматично ділить код за маршрутами.
Для додаткового поділу компонентів використовуйте next/dynamic.
Динамічний компонент можна завантажувати лише після дії користувача.
Параметр loading задає інтерфейс під час завантаження.
ssr: false потрібен для компонентів, які не можуть виконуватися на сервері.
Не використовуйте динамічний імпорт для кожного маленького компонента.
Найкращий результат дає поєднання Server Components, невеликих Client Components і динамічного завантаження великих або рідко потрібних частин інтерфейсу.