index.html: Чому класичний Client-Side React програє в SEO та FCP.Огляд фреймворку Next.js як розширення можливостей React. Порівняння стратегій рендерингу: Static Site Generation (SSG), Server-Side Rendering (SSR) та Incremental Static Regeneration (ISR). Вивчення концепції React Server Components (RSC) як майбутнього веб-розробки.
Класичний React є клієнтською бібліотекою (Client-Side Rendering). Це означає, що при першому запиті до сайту браузер користувача отримує практично порожній HTML-документ (<div id="root"></div>) і важкий файл bundle.js. Поки цей JavaScript не завантажиться, не розпарситься і не виконається V8-рушієм, користувач бачить білий екран. Крім того, пошукові боти (Googlebot) історично погано індексували такі сторінки.
Для вирішення цих кардинальних інженерних проблем у 2016 році компанія Vercel створила Next.js — повноцінний фреймворк поверх бібліотеки React, який дозволив перенести частину процесу рендерингу (обчислення JSX) назад на сервер. Ця лекція присвячена стратегіям рендерингу, які є фундаментом сучасної архітектури високонавантажених Frontend-систем.
Уявімо інтернет-магазин на класичному React (CSR). Шлях користувача:
Loading Spinner.useEffect за товарами (800ms).Метрика FCP (First Contentful Paint) — час до того, як користувач побачить першу осмислену намальовану деталь, складає більше 2-х секунд! Для мобільних інтернет-мереж це призводить до відтоку 50% клієнтів ще до завантаження сайту.
SEO боти також “бачать” перші 2 секунди порожній div, і сайт втрачає позиції в Google.
Щоб вирішити це, ми маємо повернути користувачу готовий HTML з товарами ще на кроці №1. Для цього і потрібен Next.js.
Next.js — це фреймворк для Production-рішень. Офіційна команда React у своїй документації рекомендує розпочинати нові проєкти саме з Next.js (чи Remix), а не з create-react-app або vite.
Що Next.js дає “з коробки”, чого не має голий React:
pages/ чи app/ стає URL-адресою).SSG — це коли HTML генерується ТІЛЬКИ ОДИН РАЗ — ПІД ЧАС БІЛДУ ДОДАТКУ (на етапі npm run build на CI/CD сервері), і потім цей готовий index.html віддається всім мільйонам користувачів з блискавичною швидкістю через CDN (Content Delivery Network).
Ідеально для: Блогів, сторінок “Про компанію”, Документації, Посадкових сторінок (Landing pages). Мінус: Якщо ви опублікували нову статтю в базу даних, вам потрібно повністю “перебілдити” весь додаток, щоб вона з’явилася на сайті.
(Приклад на базі класичного Pages Router)
// Ця сторінка відрендериться 1 РАЗ при деплої
export default function Blog({ posts }) {
return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
);
}
// Ця функція викликається Next.js ТІЛЬКИ ПІД ЧАС КОМПІЛЯЦІЇ СЕРВЕРА
export async function getStaticProps() {
const res = await fetch("https://api.github.com/repos/vercel/next.js/issues");
const posts = await res.json();
// Повертаємо пропси, з якими Next.js "заморозить" HTML
return { props: { posts } };
}
SSR — це коли HTML генерується КОЖЕН РАЗ, КОЛИ КОРИСТУВАЧ РОБИТЬ ЗАПИТ ДО СТОРІНКИ (у реальному часі на сервері Node.js).
Ідеально для: Стрічки новин (Twitter/X), Кошика користувача, Інформашбних панелей адміністратора, будь-яких даних, що критично прив’язані до Cookie авторизації або метрик у реальному часі.
Мінус: Високе навантаження на сервер (CPU). Коментар Time-To-First-Byte (TTFB) збільшується, бо серверу треба час на виконання fetch до бази і генерації стрінгів HTML.
export default function Dashboard({ userStats }) {
return <div>Рівень: {userStats.level}</div>;
}
// Функція викликатиметься ПРИ КОЖНОМУ заході будь-якого користувача
export async function getServerSideProps(context) {
// context.req містить cookies, заголовки, IP адресу
const sessionUser = await checkAuthTokens(context.req.cookies.token);
const res = await fetch(`https://api.mygame.com/stats/${sessionUser.id}`);
const userStats = await res.json();
return { props: { userStats } };
}
ISR — це інженерний шедевр Next.js. Це спроба взяти блискавичну швидкість SSG і поєднати з динамікою SSR. При ISR ви генеруєте сторінки як статичні (SSG), але паралельно “кажете” Next.js: “оновлюй ці сторінки у фоні на сервері кожні X секунд, якщо будуть надходити запити”.
Ідеально для: Карток товарів в інтернет-магазині (ціна змінюється рідко, але потрібно, щоб протягом дня вона підтягнулася без перебілду сайту).
export async function getStaticProps() {
const res = await fetch("https://api.store.com/product/123");
const product = await res.json();
return {
props: { product },
// МАГІЯ ISR:
// Next.js буде віддавати кешований HTML з білду перші 60 секунд.
// Якщо прийде користувач на 61-й секунді, Next віддасть йому старий HTML,
// АЛЕ у фоні ініціює новий запит на API, згенерує новий HTML і збереже його в кеш.
// Всі наступні користувачі отримають вже новий HTML.
revalidate: 60,
};
}
Одне з найскладніших питань на Senior-інтерв’ю: “Як працює Гідратація?”
Коли браузер отримує HTML від Next.js (через SSR або SSG), він бачить просто місиво візуальних тегів і текстів. Наприклад, кнопка <button class="btn">Click me</button> є візуально на екрані, але вона мертва, вона не має прив’язаної JavaScript функції onClick, бо браузер ще не знає, що це код на React.
Процес “воскресіння” сторінки називається Гідратацією (Hydration):
Event Listeners (onClick, onChange).Дійшли до сучасності. Використання SSR та Гідратації має фундаментальний недолік: нам доводиться відправляти ВЕСЬ JavaScript код компонентів на клієнт (разом з HTML), щоб React міг провести гідратацію. Чим складніший UI, тим товще бандл (Megabytes of JS).
Впровадження архітектури App Router у Next.js 13 реалізувало концепцію React Server Components (RSC).
RSC — це компоненти, які рендеряться виключно на сервері. Їх JavaScript код (і код їхніх бібліотек, як наприклад date-fns чи Markdown-парсерів) НІКОЛИ не відправляється в браузер.
useState, useEffect, onClick події (бо немає JS у браузері на їх обробку).await fetch() або звернення до Бази Даних, бо вони на сервері!Якщо вам потрібна кнопка з onClick, ви створюєте звичайний “Клієнтський” компонент (через директиву "use client";) і вставляєте його всередину Серверного компонента як острівець інтерактивності (Islands Architecture).
// app/page.tsx (Server Component за замовчуванням у Next.js API Router)
// ЦЕЙ КОД І БІБЛІОТЕКИ ВИКОНУЮТЬСЯ ТІЛЬКИ НА СЕРВЕРІ! НІЯКОГО JS У БРАУЗЕРІ ПРО ЦЕЙ КОМПОНЕНТ.
import db from "./database";
import ClientLikeButton from "./ClientLikeButton";
export default async function ProductPage() {
// ПРЯМИЙ ЗАПИТ У БАЗУ (без Axios/Fetch Api Layer!)
const product = await db.query("SELECT * FROM products WHERE id=1");
return (
<div>
<h1>{product.name}</h1>
<p>{product.description}</p>
{/* Клієнтський острівець для інтерактивності (тільки його JS код полетить у браузер) */}
<ClientLikeButton id={product.id} />
</div>
);
}
Підсумок RSC: Ми отримуємо найкраще з обох світів — блискавичний HTML з прямим доступом до бази на сервері і нульовим розміром JS бандлу, при цьому зберігаємо клієнтську реактивність React-у в місцях “острівців” (кнопок, форм).
fetch-запитів на сервера Node.js, генеруючи свіжий HTML на кожне звернення браузера. Це вирішує проблему персоналізації, проте підвищує фінансові витрати на оренду серверних потужностей (Computational Cost).bundle.js (5-та секунда)?useState та useContext фізично заборонене (буде викликана помилка інструментом компіляції) в архітектурі React Server Components (RSC)? Опишіть обмеження серверного середовища для таких стейтів."use client";) на повністю серверній сторінці перегляду “Новини”.