Пошук уроків, статей та іншого контенту
Виберете стратегію завантаження скриптів і визначите момент їх виконання без блокування основного контенту.
JavaScript-скрипти можуть впливати на швидкість завантаження сторінки двома способами:
браузер витрачає час на завантаження файлу;
виконання JavaScript займає основний потік браузера.
Якщо завантажити всі скрипти одразу, вони можуть затримати:
відображення основного контенту;
гідрацію React-компонентів;
реакцію інтерфейсу на дії користувача;
метрики продуктивності, наприклад Largest Contentful Paint та Total Blocking Time.
У Next.js для керування скриптами використовується компонент Script з пакета next/script.
import Script from 'next/script';
export default function Page() {
return (
<main>
<h1>Каталог товарів</h1>
<p>Цей контент може відображатися незалежно від другорядного скрипту.</p>
<Script
src="/scripts/feedback-widget.js"
strategy="lazyOnload"
/>
</main>
);
}На відміну від звичайного HTML-елемента <script>, next/script допомагає Next.js визначити оптимальний момент завантаження та виконання скрипту.
Компонент Script підтримує кілька стратегій. Найчастіше використовують такі:
beforeInteractive;
afterInteractive;
lazyOnload.
beforeInteractiveСкрипт завантажується до гідрації сторінки та до виконання основної логіки Next.js.
<Script
src="/scripts/critical-script.js"
strategy="beforeInteractive"
/>Ця стратегія призначена лише для скриптів, які дійсно необхідні до того, як сторінка стане інтерактивною.
Приклади можливого використання:
скрипт безпеки, який має виконатися до взаємодії користувача;
скрипт, необхідний для початкової конфігурації;
скрипт, який повинен бути доступним ще до гідрації.
У App Router такий скрипт зазвичай розміщують у кореневому app/layout.tsx. У Pages Router його розміщують у _document.tsx.
beforeInteractiveне означає «найкраща стратегія для всіх скриптів». Такий скрипт завантажується дуже рано й може конкурувати з критичними ресурсами сторінки.
afterInteractiveСкрипт завантажується після того, як сторінка почала ставати інтерактивною. Це стандартна стратегія, якщо strategy не вказано.
<Script
src="https://example.com/analytics.js"
strategy="afterInteractive"
/>Цю стратегію можна використовувати для:
аналітики;
маркетингових скриптів;
інтеграцій, які потрібні на поточній сторінці;
віджетів, без яких основний контент все одно працює.
Приклад:
import Script from 'next/script';
export default function ProductPage() {
return (
<main>
<h1>Ноутбук</h1>
<p>Опис товару та його характеристики.</p>
<Script
src="/scripts/product-analytics.js"
strategy="afterInteractive"
/>
</main>
);
}afterInteractive добре підходить для скриптів, які потрібні відносно швидко, але не повинні блокувати початкове відображення HTML.
lazyOnloadСкрипт завантажується після події load, коли браузер завершив завантаження основних ресурсів сторінки. Next.js намагається виконати його під час простою браузера.
<Script
src="/scripts/feedback-widget.js"
strategy="lazyOnload"
/>Використовуйте цю стратегію для функцій, які не потрібні одразу:
віджетів зворотного зв’язку;
чатів підтримки;
соціальних віджетів;
додаткових маркетингових інструментів;
другорядної аналітики.
Якщо користувач може прочитати сторінку та виконати основну дію без скрипту, найімовірніше, для нього підходить lazyOnload.
Нижче наведено приклад для App Router. Основний контент відображається одразу, аналітичний скрипт завантажується після початку взаємодії, а віджет зворотного зв’язку — ще пізніше.
app/layout.tsximport type { Metadata } from 'next';
import Script from 'next/script';
import type { ReactNode } from 'react';
export const metadata: Metadata = {
title: 'Магазин',
description: 'Приклад оптимізованого завантаження скриптів',
};
export default function RootLayout({
children,
}: Readonly<{
children: ReactNode;
}>) {
return (
<html lang="uk">
<body>
{children}
<Script
src="/scripts/analytics.js"
strategy="afterInteractive"
/>
<Script
src="/scripts/feedback-widget.js"
strategy="lazyOnload"
/>
</body>
</html>
);
}app/page.tsxexport default function HomePage() {
return (
<main>
<h1>Магазин електроніки</h1>
<p>
Основний контент сторінки не залежить від аналітики чи віджета.
</p>
<button type="button">
Переглянути товари
</button>
</main>
);
}public/scripts/analytics.js(() => {
const event = {
type: 'page_view',
path: window.location.pathname,
timestamp: new Date().toISOString(),
};
console.log('Аналітична подія:', event);
})();public/scripts/feedback-widget.js(() => {
const button = document.createElement('button');
button.type = 'button';
button.textContent = 'Зворотний зв’язок';
Object.assign(button.style, {
position: 'fixed',
right: '16px',
bottom: '16px',
zIndex: '1000',
padding: '10px 14px',
border: '0',
borderRadius: '8px',
background: '#111827',
color: '#ffffff',
cursor: 'pointer',
});
button.addEventListener('click', () => {
window.alert('Форма зворотного зв’язку відкриється тут.');
});
document.body.append(button);
})();У цьому прикладі:
сторінка та кнопка відображаються без очікування додаткових скриптів;
аналітика запускається після того, як сторінка стала інтерактивною;
віджет зворотного зв’язку не конкурує з початковим завантаженням сторінки.
Перед додаванням скрипту поставте такі запитання:
Чи може сторінка коректно відобразитися без цього скрипту?
Чи потрібен скрипт до гідрації React?
Чи потрібен він одразу після того, як користувач може взаємодіяти зі сторінкою?
Чи можна завантажити його після повного завантаження сторінки?
Орієнтовний вибір:
якщо без скрипту неможлива початкова робота сторінки — beforeInteractive;
якщо скрипт потрібен незабаром після початку взаємодії — afterInteractive;
якщо скрипт другорядний — lazyOnload.
Більшість сторонніх скриптів не потребують beforeInteractive.
Скрипт можна розмістити на рівні всього застосунку або лише на конкретній сторінці.
Розміщуйте його в app/layout.tsx, якщо він потрібен на всіх сторінках:
import Script from 'next/script';
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="uk">
<body>
{children}
<Script
src="/scripts/analytics.js"
strategy="afterInteractive"
/>
</body>
</html>
);
}Якщо скрипт потрібен лише на одній сторінці, розмістіть його в цьому маршруті:
import Script from 'next/script';
export default function CheckoutPage() {
return (
<main>
<h1>Оформлення замовлення</h1>
<Script
src="/scripts/payment-widget.js"
strategy="afterInteractive"
/>
</main>
);
}Так скрипт не завантажуватиметься на сторінках, де він не використовується.
Для невеликого inline-скрипту можна використати id та dangerouslySetInnerHTML.
import Script from 'next/script';
export default function Page() {
return (
<main>
<h1>Профіль користувача</h1>
<Script
id="profile-config"
strategy="afterInteractive"
dangerouslySetInnerHTML={{
__html: `
window.profileConfig = {
theme: 'light'
};
`,
}}
/>
</main>
);
}Для inline-скриптів потрібен унікальний id. Проте не варто використовувати такий підхід для великих блоків JavaScript. Великі скрипти краще зберігати в окремих файлах або підключати через src.
lazyOnload і afterInteractive переносять завантаження скрипту на пізніший момент, але сам JavaScript все одно виконується в основному потоці браузера.
Тому навіть відкладений скрипт може тимчасово заблокувати інтерфейс, якщо він:
великий;
виконує складні обчислення;
створює багато DOM-вузлів;
запускає довгі синхронні операції.
Стратегія вирішує проблему моменту завантаження, але не замінює оптимізацію самого скрипту.
Уявімо сторінку товару:
основна інформація про товар потрібна одразу;
аналітика потрібна для відстеження перегляду;
чат підтримки потрібен лише частині користувачів;
платіжний віджет потрібен лише на сторінці оформлення замовлення.
Тоді рішення може бути таким:
import Script from 'next/script';
export default function ProductPage() {
return (
<main>
<h1>Смартфон</h1>
<p>Основний опис товару.</p>
{/* Аналітика потрібна відносно рано, але не для початкового HTML */}
<Script
src="/scripts/analytics.js"
strategy="afterInteractive"
/>
{/* Чат не потрібен для першої взаємодії зі сторінкою */}
<Script
src="/scripts/chat.js"
strategy="lazyOnload"
/>
</main>
);
}beforeInteractive у такому сценарії не потрібна, оскільки жоден із цих скриптів не впливає на початкове відображення товару.
beforeInteractive для всіх скриптів<Script
src="/scripts/analytics.js"
strategy="beforeInteractive"
/>Аналітика зазвичай не потрібна до гідрації. Раннє завантаження може створити зайве мережеве та процесорне навантаження.
Краще:
<Script
src="/scripts/analytics.js"
strategy="afterInteractive"
/>Якщо віджет використовується лише на сторінці підтримки, не додавайте його до кореневого layout. Інакше він завантажуватиметься на всіх маршрутах.
Не підключайте той самий файл одночасно в layout і в окремому компоненті. Це може призвести до повторної ініціалізації аналітики, віджета або обробників подій.
lazyOnload виконається негайноlazyOnload призначений для другорядних функцій. Не використовуйте його для логіки, без якої користувач повинен одразу виконати дію.
Якщо кнопка, форма або навігація працює лише після завантаження lazyOnload-скрипту, користувач може побачити інтерфейс, який ще не готовий до роботи.
Основні можливості сторінки мають працювати без другорядних скриптів або мати зрозумілий стан завантаження.
Знайдіть усі сторонні та власні скрипти на сторінці.
Визначте, які з них потрібні для початкової роботи.
Для критичних скриптів розгляньте beforeInteractive.
Для скриптів, потрібних після початку взаємодії, використовуйте afterInteractive.
Для другорядних віджетів використовуйте lazyOnload.
Розміщуйте скрипт якомога ближче до маршруту, де він потрібен.
Перевірте, що основний контент та основні дії не залежать від відкладених скриптів.
Перевірте продуктивність у production-збірці, оскільки режим розробки не відображає реальну поведінку завантаження.
У Next.js для скриптів використовується компонент Script з next/script.
beforeInteractive призначена для рідкісних критичних скриптів.
afterInteractive є стандартним вибором для аналітики та інтеграцій, потрібних після початку взаємодії.
lazyOnload підходить для чатів, віджетів і другорядних функцій.
Скрипти, потрібні лише на окремій сторінці, не слід розміщувати в глобальному layout.
Відкладене завантаження зменшує вплив на початкове відображення, але не усуває витрати на виконання JavaScript.
Правильна стратегія дозволяє показати основний контент раніше та не блокувати першу взаємодію користувача.