Серверный рендеринг с Preact
Если вы создавали приложения с использованием React и Vite, вы уже знакомы с клиентским рендерингом. Браузер скачивает JavaScript-бандл, выполняет его и строит DOM. Это работает, но пользователи с медленным соединением вынуждены смотреть на пустой экран, пока загружается бандл. Серверный рендеринг (SSR) с Preact решает эту проблему, отправляя готовый к отображению HTML с сервера. Небольшой размер рантайма Preact делает этот компромисс особенно привлекательным для приложений, ориентированных на производительность.
В этой статье объясняется, как работает Preact SSR, какие инструменты задействованы и как гидратация связывает серверный HTML с живым интерактивным приложением.
Ключевые выводы
- Preact SSR рендерит дерево компонентов в HTML-строку на сервере, предоставляя пользователям видимый контент до выполнения какого-либо JavaScript.
- Пакет
preact-render-to-stringпредоставляет синхронные, асинхронные и потоковые API рендеринга для разных потребностей приложений. - Гидратация добавляет интерактивность к серверному HTML с помощью
hydrate, а неrender, сохраняя существующий DOM. - Workflow на базе Vite с
@preact/preset-vite— современный стандарт для новых проектов Preact с поддержкой SSR. - Избегайте несоответствий гидратации, обеспечивая согласованность вывода сервера и клиента — никаких временных меток, случайных ID или обращений к
windowво время серверного рендеринга.
Что такое Preact SSR и почему это важно?
При SSR дерево компонентов рендерится в HTML-строку на сервере ещё до того, как браузер что-либо получит. Пользователь видит контент мгновенно. JavaScript загружается в фоне и затем добавляет интерактивность.
Практические преимущества вполне реальны:
- Более быстрое воспринимаемое время загрузки — видимый контент приходит с первым HTTP-ответом.
- Лучшее SEO — поисковые роботы читают полностью сформированный HTML без выполнения JavaScript.
- Устойчивость — страницы остаются читаемыми даже при сбоях или медленной загрузке клиентского JS.
Preact особенно хорошо подходит для SSR благодаря небольшому рантайму, добавляющему минимальные накладные расходы.
Основной инструмент: preact-render-to-string
Preact SSR зависит от preact-render-to-string — отдельного пакета, который выполняет серверный рендеринг.
npm install preact-render-to-string
Синхронный рендеринг
Для компонентов без асинхронных зависимостей renderToString за один проход преобразует дерево компонентов в HTML:
import { renderToString } from 'preact-render-to-string';
const App = () => <h1>Hello from the server</h1>;
const html = renderToString(<App />);
// → <h1>Hello from the server</h1>
Асинхронный рендеринг
Когда компоненты загружают данные или используют Suspense с ленивой загрузкой чанков, используйте renderToStringAsync. Он ожидает завершения промисов и асинхронной работы рендеринга, прежде чем вернуть финальную HTML-строку.
import { renderToStringAsync } from 'preact-render-to-string';
const html = await renderToStringAsync(<App />);
Потоковая передача
Для крупных страниц потоковые API позволяют отправлять HTML в браузер по частям по мере рендеринга каждого раздела. renderToPipeableStream ориентирован на потоки Node.js, а renderToReadableStream — на окружения, использующие Web Streams API, включая такие платформы, как Cloudflare Workers, Deno и Bun. Потоковая передача улучшает Time to First Byte (TTFB), не дожидаясь завершения полного рендеринга.
Гидратация Preact: связь HTML с интерактивностью
Отправка статического HTML — лишь половина дела. Чтобы сделать страницу интерактивной, Preact должен гидратировать существующий DOM — присоединить обработчики событий и состояние без повторного рендеринга с нуля.
import { hydrate } from 'preact';
import { App } from './app.js';
hydrate(<App />, document.getElementById('root'));
Используйте hydrate вместо render, когда DOM уже сформирован сервером. Использование render отбросит серверный HTML и пересоберёт его заново, что сведёт на нет весь смысл.
Несоответствия гидратации возникают, когда серверный HTML не совпадает с тем, что отрендерил бы клиент. Распространённые причины — рендеринг временных меток, случайных ID или чтение window во время серверного прохода. Поддерживайте логику серверных и клиентских компонентов согласованной, чтобы этого избежать.
Discover how at OpenReplay.com.
Preact Vite SSR: современная настройка
Для новых проектов workflow на базе Vite — практичный выбор по умолчанию. Руководство Vite по SSR описывает паттерн двойной сборки: одна сборка для серверной точки входа и другая — для клиентского бандла. Preact чисто интегрируется с @preact/preset-vite, который берёт на себя JSX, алиасы и специфичную для Preact настройку.
Командам, которым нужна более структурированная отправная точка, preact-iso предоставляет легковесные утилиты маршрутизации и предварительного рендеринга, разработанные специально для проектов Preact Vite.
О чём стоит помнить
Preact SSR концептуально схож с React SSR, но не идентичен ему во всех деталях реализации. Некоторые специфичные для React SSR API не имеют прямых эквивалентов, а экосистема вокруг Preact меньше — это справедливая плата за выигрыш в размере и производительности.
Надёжно работающий паттерн: загрузите данные перед рендерингом, передайте их через props, отрендерите в строку на сервере, гидратируйте на клиенте. Начните с этого, а затем подключайте потоковую передачу или более продвинутые паттерны рендеринга по мере необходимости.
Заключение
Preact SSR предлагает лёгкий, ориентированный на производительность путь к серверно-рендерным приложениям без накладных расходов более крупного фреймворка. Сочетая preact-render-to-string для серверного прохода, hydrate для активации на клиенте и Vite для пайплайна сборки, вы получаете настройку, которую легко запустить и в которой легко разобраться. Начните с базового потока render-and-hydrate, поддерживайте согласованность вывода сервера и клиента, а к потоковой передаче обращайтесь только тогда, когда масштаб приложения это оправдывает.
FAQ
Выбирайте Preact SSR, когда размер бандла, скорость холодного старта или edge-развёртывание важнее доступа к более широкой экосистеме React. Небольшой рантайм Preact идеален для контентных сайтов, маркетинговых страниц и развёртываний на воркерах. Оставайтесь с React SSR, если вы зависите от React-специфичных библиотек или нуждаетесь в таких API, как React Server Components, которые Preact не воспроизводит.
Убедитесь, что сервер и клиент рендерят одинаковый вывод для одних и тех же props. Избегайте недетерминированных значений во время начального рендеринга, таких как Date.now, Math.random или доступных только в браузере глобальных объектов вроде window и localStorage. Если нужен контент только для клиента, рендерите стабильный плейсхолдер на сервере и обновляйте его внутри эффекта после завершения гидратации.
Многие React-библиотеки работают через алиас preact/compat, который сопоставляет импорты React с эквивалентами Preact. Однако библиотеки, зависящие от React-специфичных возможностей SSR, таких как React Server Components или внутренние механизмы рендеринга, могут вести себя некорректно. Тестируйте каждую зависимость в вашем SSR-пайплайне, прежде чем закрепить её в проекте.
Скорее всего, нет. Потоковая передача окупается, когда страницы большие, насыщены данными или содержат разделы, которые загружаются с разной скоростью. Для типичного небольшого сайта renderToString или renderToStringAsync производят результат достаточно быстро, чтобы потоковая передача лишь добавляла сложности без ощутимой пользы. Начните с более простых синхронных или асинхронных API и переходите на потоковую передачу только тогда, когда TTFB станет реальным узким местом.
Gain Debugging Superpowers
Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers. Check our GitHub repo and join the thousands of developers in our community.