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'ов. Браузер начинает рисовать страницу, не дожидаясь конца ответа.

АспектКлассический SSRStreaming 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
cacheSignal и streaming SSR

0

1

Войти