React
cacheSignal и streaming SSR
Страница состоит из быстрого заголовка и медленной ленты, которой нужен запрос к базе на 800 мс. Классический SSR держит весь HTML заложником самой медленной части: пока лента не готова, пользователь видит белый экран. Streaming SSR отдаёт заголовок сразу, а на месте ленты ставит fallback, и дострелливает её HTML отдельным куском, когда данные приедут. React 19.2 добавил к этому cacheSignal - сигнал, который обрывает уже ненужную серверную работу, если её результат всё равно протух.
- React 18+: streaming SSR через renderToPipeableStream и Suspense - штатный механизм отдачи HTML по частям
- Next.js App Router: loading.tsx и Suspense дают потоковую отдачу и выборочную гидрацию из коробки
- Vercel: Partial Pre-rendering объединяет статическую оболочку и динамические дырки в одном потоковом ответе
- React 19.2 (октябрь 2025): cacheSignal позволяет отменять серверную работу, когда её кэш-запись истекла
- Amazon, крупные e-commerce: потоковая отдача шапки и каталога до готовности персональных блоков ускоряет первый показ
Предварительные знания
- Server Components и граница сервер-клиент, серверный рендеринг
- Suspense: границы загрузки и fallback для приостановленных поддеревьев
- Понятие гидрации: как клиентский JS оживляет уже отрисованный сервером HTML
Как SSR научился отдавать HTML по частям
Ранний SSR в React был синхронным: renderToString собирал весь HTML за один проход и отдавал целиком. Одна медленная часть тормозила всю страницу, а гидрация была всё-или-ничего - пока не загрузился весь JS, ничего не интерактивно. React 18 (2022) принёс архитектуру конкурентного рендеринга и потоковый renderToPipeableStream. Теперь сервер отдаёт готовые части немедленно, а для медленных ставит Suspense-границу и дострелливает их позже отдельным чанком. Гидрация стала выборочной: интерактивность включается по кускам, а не разом. React 19.2 (октябрь 2025) добавил cacheSignal, чтобы серверная работа, чей кэш истёк во время рендеринга, могла быть честно прервана, а не доедала ресурсы впустую.
Streaming SSR: HTML по частям
При классическом SSR сервер собирает весь HTML и отдаёт его одним блоком - страница появляется не раньше, чем готова самая медленная её часть. Streaming SSR разрывает эту связку. Сервер отдаёт готовый HTML немедленно, а каждую медленную часть оборачивает в Suspense: на её месте сначала уходит fallback, а настоящий HTML дострелливается отдельным чанком, когда данные подтянутся. Пользователь видит каркас страницы за миллисекунды, а не за время самого медленного запроса.
Механически это работает так: сервер использует потоковый рендер (renderToPipeableStream на Node или renderToReadableStream на edge). Готовый HTML отправляется в ответ сразу, а для приостановленных Suspense-границ в поток позже дописываются куски, которые на клиенте встают на место fallback'ов. Браузер начинает рисовать страницу, не дожидаясь конца ответа.
| Аспект | Классический SSR | Streaming SSR |
|---|---|---|
| Когда уходит первый HTML | Когда готова вся страница | Сразу, готовые части первыми |
| Медленная часть | Блокирует всю отдачу | Показывает fallback, дострелливается потоком |
| Гидрация | Всё или ничего | Выборочная, по частям |
Streaming не делает суммарную работу меньше - данные ленты всё равно нужно загрузить. Он меняет воспринимаемую скорость: пользователь раньше видит контент и каркас, а медленные блоки приезжают позже, не задерживая остальное. Это про время до первого показа, а не про общую нагрузку.
Что streaming SSR делает с медленной частью страницы, обёрнутой в Suspense?
Выборочная гидрация
Гидрация - это процесс, в котором клиентский JS навешивает обработчики на уже отрисованный сервером HTML, делая его интерактивным. В старом SSR гидрация была всё-или-ничего: пока не загрузился и не отработал весь бандл, ни одна кнопка не реагировала. Выборочная гидрация разбивает её по Suspense-границам: каждое поддерево оживляется независимо, и React расставляет приоритеты.
Два следствия делают выборочную гидрацию мощной. Во-первых, готовые части становятся интерактивными, не дожидаясь медленных. Во-вторых, React приоритизирует гидрацию по взаимодействию: если пользователь кликнул в ещё негидрированную область, React гидрирует именно её первой, обрабатывая клик как можно раньше. Suspense-граница оказывается одновременно единицей потоковой отдачи и единицей гидрации.
Suspense-границы стоит расставлять по логическим блокам с разной скоростью данных: быстрый заголовок отдельно, медленная лента отдельно, сайдбар отдельно. Тогда каждый блок и отдаётся потоком, и гидрируется независимо, а не тянет соседей за собой.
Чем выборочная гидрация отличается от прежней модели гидрации?
cacheSignal и Partial Pre-rendering
cacheSignal, появившийся в React 19.2, решает узкую, но важную задачу: как прервать серверную работу, результат которой уже не нужен. Он возвращает AbortSignal, привязанный к жизни кэш-записи. Пока запись актуальна, сигнал не сработал. Как только запись истекает или отменяется, сигнал срабатывает (abort), и любая работа, подписанная на него - запрос к базе, fetch, тяжёлое вычисление - может быть честно прервана, а не доедать ресурсы впустую.
Partial Pre-rendering (PPR) - концепция, которая соединяет streaming и кэш в единую модель. Статическую оболочку страницы (шапка, разметка, всё, что не зависит от запроса) предрендеривают заранее и отдают мгновенно как готовый HTML. Динамические части (персональные данные, корзина) оставляют 'дырками' под Suspense и достреливают потоком в том же ответе. Пользователь получает мгновенный статический каркас и свежие динамические данные в одном запросе, без выбора между 'быстро, но устарело' и 'свежо, но медленно'.
- Полностью статическая страница — Мгновенная отдача из кэша, но данные те же для всех и могут устареть. Не подходит для персонального и часто меняющегося контента.
- Partial Pre-rendering — Статическая оболочка отдаётся мгновенно, динамические дырки под Suspense достреливаются потоком. Каркас сразу, персональные данные следом, в одном ответе.
В 2026 году Partial Pre-rendering - это в первую очередь возможность фреймворка (Next.js строит её поверх streaming и кэша React), а cacheSignal - низкоуровневый примитив React 19.2, на который такая инфраструктура опирается, чтобы не тратить серверное время на отменённую или протухшую работу.
Что представляет собой cacheSignal из React 19.2?
Связь с другими темами
Эта тема про производительность серверного рендеринга. Что рядом:
- Suspense и lazy — Границы Suspense размечают, что можно отдать сразу, а что дострелить потоком позже
- Server Components — Серверное окружение, где идёт потоковый рендеринг и где живёт кэш с cacheSignal
- Next.js App Router — Фреймворк, реализующий streaming и Partial Pre-rendering поверх примитивов React
Итог
- Streaming SSR отдаёт HTML по частям: готовые куски уходят немедленно, а медленные за Suspense-границей дострелливаются позже отдельными чанками
- Каждая Suspense-граница - это и точка, до которой можно отдать HTML, и единица выборочной гидрации: интерактивность включается по кускам
- Выборочная гидрация оживляет части страницы независимо и по приоритету, а не ждёт, пока загрузится весь клиентский JS
- cacheSignal (React 19.2) - это AbortSignal, привязанный к кэш-записи: когда запись истекает, сигнал срабатывает и зависимую работу можно прервать
- Partial Pre-rendering объединяет статически предрендеренную оболочку и динамические дырки в одном потоковом ответе: мгновенный каркас плюс свежие данные потоком
Связанные уроки
- rc-30-rsc-intro — Streaming и cacheSignal живут в серверном окружении RSC и опираются на границу сервер-клиент
- rc-21-lazy-suspense — Streaming SSR строится на Suspense: каждая граница - точка, до которой HTML отдаётся, и место выборочной гидрации
- rc-35-nextjs-app-router — App Router реализует streaming, Partial Pre-rendering и кэш поверх этих механизмов React