Рефакторинг SSR: renderToString и renderToPipeableStream в React 19
Рефакторинг SSR: renderToString и renderToPipeableStream в React 19 Долгое время renderToString был монолитом: блокировал event loop до полного выплеска HTML, игнорируя Suspense-границы. Результат — пустая страница для пользователя, пока не загрузится всё. React 19 переосмысливает этот подход, добавляя гранулярное кэширование фрагментов и асинхронную потоковую отдачу. Как Suspense ломает статус-кво В React 19 renderToString научился работать с Suspense через механизм компенсированного выброса. Идея: React выдаёт готовый HTML, а то, что висит на Suspense, досылает через renderToPipeableStream. Это означает, что TTFB падает на 40-60%: не нужно ждать все данные сразу, браузер получает каркас страницы, а остальные куски с данными подгружаются постепенно. Гранулярное кэширование: не трогай статику Ключевая фича — кэширование отдельных Suspense-границ. Берёшь «шапку» и «блок комментариев» и кэшируешь их по отдельности. Если в комментариях данные устарели, шапка берётся из кэша без перерендера. Пример: const fragmentCache = new Map(); function renderPage(req, res) { const stream = renderToPipeableStream( <Page userId={req.userId} />, { onShellReady() { res.write(''); stream.pipe(res); }, onFragmentComplete(id, html) { fragmentCache.set(id, html); } } ); } Здесь onFragmentComplete даёт доступ к HTML каждого фрагмента по его id. Можно складывать в Map, Redis или CDN. Статичные блоки (шапка, подвал) перестают рендериться заново, что снижает нагрузку на сервер. Типичная ошибка: игнорировать React Flight Внутри React 19 renderToPipeableStream использует React Flight: режет HTML на чанки, каждый фрагмент — это свой Promise. Когда данные готовы, отправляется чанк. Старый renderToNodeStream выпилили. Ошибка — продолжать использовать renderToString для страниц с динамическими частями (лента, комментарии, графики). Это приводит к блокировке event loop и росту TTFB, особенно при высоком RPS. Практический совет: начинай с анализа Не беги внедрять гранулярное кэширование на каждую страницу. Для простых статичных страниц это оверинжиниринг. Но если у тебя продуктовая страница с частыми обновлениями (например, корзина или рекомендации), профит очевиден. Используй профилировщик React (например, React DevTools Profiler) и измеряй TTFB и TBT до и после. Оптимизируй только узкие места. Вывод: Гранулярное кэширование фрагментов в React 19 через Suspense и renderToPipeableStream — это production-ready паттерн для снижения TTFB и серверной нагрузки на динамических страницах, но требует вдумчивого выбора кандидатов для оптимизации.