Гидратация: SSR, mismatch и Streaming в React
Гидратация — мост между серверным HTML и интерактивным React-приложением. Сервер отдаёт готовый HTML, браузер загружает JS-бандл, и React «навешивает» обработчики на существующие DOM-узлы, не пересоздавая их. Без понимания этого процесса сложно избежать hydration mismatch и оптимизировать TTI.
Содержание
- Что такое гидратация
- Как работает гидратация
- Почему гидратация важна
- Проблемы классической гидратации
- React 18: Streaming SSR и Selective Hydration
- React 19 и Partial Pre-rendering
- Способы уменьшить влияние гидратации
- Когда гидратация не нужна
- Ключевые тезисы для интервью
- Заключение
Что такое гидратация
Гидратация (hydration) — это процесс подключения JavaScript к уже отрендеренному статическому HTML на клиенте, чтобы сделать страницу интерактивной. Сервер отдаёт полный HTML, браузер загружает JS-бандл React, и React «навешивает» на существующие DOM-узлы обработчики событий, состояние и эффекты.
Без гидратации страница выглядела бы как статичный манекен: виден, но не реагирует.
Как работает гидратация
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Сервер │ ───► │ Браузер │ ───► │ React │
│ отдаёт HTML│ │ получает │ │ гидратирует│
│ + данные │ │ HTML + JS │ │ DOM │
└─────────────┘ └─────────────┘ └─────────────┘
Шаг 1: Серверный рендеринг
Сервер генерирует полный HTML страницы и отправляет его браузеру. Пользователь сразу видит контент, но страница ещё не интерактивна.
Шаг 2: Загрузка JavaScript
Браузер загружает JavaScript-бандл React и зависимости. В это время страница отображается, но кнопки и формы не работают.
Шаг 3: Подключение React
React строит виртуальное DOM-дерево, сравнивает его с существующим HTML и прикрепляет обработчики событий, состояния и эффекты к DOM-узлам. Важно: React не пересоздаёт DOM, а использует существующий.
Шаг 4: Интерактивность
После гидратации страница становится полностью интерактивной. Время от первой отрисовки до гидратации влияет на метрику TTI (Time to Interactive).
Почему гидратация важна
- Быстрая первая отрисовка — пользователь видит контент мгновенно, не дожидаясь загрузки JS.
- SEO — поисковые боты видят полный HTML.
- Интерактивность — после загрузки JS страница полностью функциональна.
- Производительность восприятия — даже при медленном соединении кажется, что сайт работает быстро.
Проблемы классической гидратации
Большой JS-бандл
Чем больше кода, тем дольше загрузка и выполнение. Это увеличивает TBT (Total Blocking Time) и задерживает интерактивность.
Hydration Mismatch
Ошибка возникает, когда HTML от сервера отличается от того, что React ожидает на клиенте. React не может корректно «подключиться» к DOM и перерендеривает страницу целиком, теряя преимущества SSR.
Частые причины
- Использование
Date.now(),Math.random()— разные значения на сервере и клиенте. - Обращение к
window,document,localStorage— недоступны на сервере. - Условный рендеринг на основе браузерных API.
- Разница в форматировании дат/чисел из-за локали.
Как избежать
- Используйте
useEffectдля кода, зависящего от браузера. - Убедитесь, что рендеринг детерминирован (одинаковый результат при одинаковых входных данных).
- Используйте
suppressHydrationWarningтолько для безобидных различий (например, временные метки). - Применяйте
typeof window !== 'undefined'только внутри эффектов, а не в рендере.
// ❌ Плохо: разный HTML на сервере и клиенте
function Page() {
const width = window.innerWidth; // window недоступен на сервере
return <div>{width}</div>;
}
// ✅ Хорошо: браузерный код в useEffect
function Page() {
const [width, setWidth] = useState(0);
useEffect(() => {
setWidth(window.innerWidth);
}, []);
return <div>{width}</div>;
}
Задержка до интерактивности
Весь HTML должен быть гидратирован, прежде чем страница станет интерактивной. На больших страницах это может занять секунды.
React 18: Streaming SSR и Selective Hydration
React 18 изменил подход к гидратации:
- Streaming SSR — HTML отправляется частями (чанками) по мере готовности компонентов.
- Selective Hydration — React гидратирует компоненты по приоритету: сначала тот, с которым взаимодействует пользователь.
// Серверный компонент потоково отдаёт HTML
import { Suspense } from 'react';
export default function Page() {
return (
<>
<Header />
<Suspense fallback={<Skeleton />}>
<Comments />
</Suspense>
</>
);
}
React 19 и Partial Pre-rendering
React 19 и Next.js 14+ развивают концепцию Partial Pre-rendering (PPR):
- Статическая оболочка страницы генерируется заранее.
- Динамические части подгружаются и гидратируются по мере необходимости.
- Уменьшается объём JS, необходимый для первой интерактивности.
Способы уменьшить влияние гидратации
- Меньше клиентского кода — выносите логику в серверные компоненты.
- Lazy loading — грузите тяжёлые компоненты динамически.
- Разделение на чанки — code splitting по маршрутам.
- Избегайте hydration mismatch — детерминированный рендеринг.
- Используйте
React.Suspense— для потоковой подгрузки частей страницы. - Рассмотрите Partial Hydration — в Next.js через Server Components.
Когда гидратация не нужна
- Чистый SSG без интерактива — например, статический блог, где не нужны хуки состояния.
- Полностью серверные компоненты — они не отправляют JS на клиент и не гидратируются.
Ключевые тезисы для интервью
- Гидратация — подключение React к серверному HTML без пересоздания DOM: сервер отдаёт HTML, браузер загружает JS, React reconciliates DOM и навешивает обработчики событий.
- Hydration Mismatch возникает, когда серверный и клиентский HTML отличаются (
Date.now(),window,Math.random()). Код, зависящий от браузерных API, нужно изолировать вuseEffect. - Большой JS-бандл увеличивает TBT и задерживает TTI — весь HTML ожидает полной загрузки и гидратации, прежде чем страница станет интерактивной.
- Streaming SSR (React 18) отправляет HTML чанками по мере готовности компонентов через Suspense; Selective Hydration гидратирует по приоритету — сначала компонент, с которым взаимодействует пользователь.
- PPR (Partial Pre-rendering): статическая оболочка генерируется при сборке, динамические части — при запросе через Suspense. Это объединяет преимущества SSG и SSR.
- Серверные компоненты не гидратируются — они не отправляют JS на клиент, что радикально уменьшает размер бандла.
Заключение
Гидратация даёт быструю первую отрисовку и SEO, но добавляет накладные расходы — JS-бандл и потенциальные hydration mismatch. Современные подходы (Streaming SSR, Selective Hydration, PPR) делают гидратацию постепенной и приоритизированной: пользователь видит контент раньше, а интерактивность появляется там, где она нужна первой.