Vite в SSR-архитектуре рассматривается не как классический серверный
фреймворк, а как слой инструментов, который разделяет ответственность
между серверной и клиентской частями приложения и управляет сборкой,
трансформацией модулей и их доставкой в двух режимах: development SSR и
production SSR.
SSR в экосистеме Vite опирается на идею двойного графа
зависимостей:
- Server Module Graph — используется для рендеринга
HTML на сервере
- Client Module Graph — используется для гидрации и
дальнейшего взаимодействия в браузере
Ключевая особенность заключается в том, что один и тот же исходный
код может иметь разные исполнения в зависимости от окружения. Это
достигается через условные экспорты и разделение входных точек:
server entry — точка входа для SSR
client entry — точка входа для гидрации
Такое разделение позволяет серверу выполнять
React/Vue/Svelte-компоненты в Node.js-окружении, а клиенту —
подхватывать уже сгенерированный HTML.
SSR-режим разработки и
роль dev-сервера
В режиме разработки Vite не выполняет полную сборку приложения.
Вместо этого используется ESM-подход с трансформацией модулей “на
лету”.
SSR в dev-режиме строится вокруг следующих принципов:
- серверный код загружается через
ssrLoadModule
- каждый модуль транспилируется по запросу
- зависимости обрабатываются через внутренний модульный граф Vite
- HMR применяется отдельно для серверного и клиентского окружений
Особое значение имеет функция ssrLoadModule, которая
позволяет загрузить модуль в Node.js с применением всех трансформаций
плагинов:
- TypeScript → JavaScript
- JSX/TSX трансформация
- импорт CSS игнорируется или маппится в noop
- условные браузерные зависимости исключаются
Таким образом SSR в dev-режиме сохраняет консистентность с
production-поведеним, но без предварительной сборки.
Production SSR и
двухэтапная сборка
В production SSR используется двухфазная модель сборки:
1. Клиентская сборка
Формируется браузерный бандл:
- код компонентов
- ассеты
- CSS extraction
- manifest.json
Манифест играет ключевую роль: он связывает серверный рендеринг с
клиентскими чанками.
2. Серверная сборка
Формируется отдельный SSR-бандл:
- исключаются браузерные API (
window,
document)
- используются Node-совместимые импорты
- сохраняется структура модулей ES
Серверный бандл обычно исполняется через ssrLoadModule
или предварительно собранный CommonJS/ESM output.
Роль manifest и
связывание HTML с клиентом
Manifest представляет собой карту зависимостей:
- какой компонент в какой файл попал
- какие чанки нужны для гидрации
- какие CSS-файлы необходимо вставить
Во время SSR генерации сервер:
- рендерит HTML строку
- определяет использованные компоненты
- извлекает соответствующие чанки из manifest
- вставляет
<script> и <link>
теги
Это позволяет минимизировать лишнюю загрузку и обеспечить точечную
гидрацию.
Гидрация и согласование
состояния
SSR в Vite всегда предполагает этап гидрации:
- сервер формирует HTML
- клиент повторно исполняет виртуальное дерево компонентов
- DOM “оживает” без полного перерендера
Ключевая проблема — hydration mismatch, возникающая
при расхождении:
- серверного состояния
- клиентского состояния
Причины:
- использование
Date.now() или Math.random()
во время рендера
- различие данных на сервере и клиенте
- асинхронная загрузка без синхронизации
Для предотвращения расхождений используется принцип:
сервер должен передавать полностью детерминированное состояние
клиенту
Обычно это реализуется через сериализацию состояния в HTML:
window.__INITIAL_STATE__
- JSON-скрипты
- встроенные payload-данные
Поток данных и data fetching
SSR в Vite предполагает, что загрузка данных должна происходить до
генерации HTML.
Существует несколько моделей:
Preload на сервере
- данные загружаются в
server entry
- передаются в компоненты через props или context
Route-level fetching
- данные привязаны к маршрутам
- сервер вызывает loader-функции до рендера
Streaming SSR
При использовании потокового рендера HTML формируется частями:
- сначала shell страницы
- затем асинхронные блоки
- затем клиентские чанки
Это снижает TTFB и улучшает perceived performance.
Изоляция окружений и
безопасные импорты
SSR в Vite требует строгого разделения окружений:
- сервер не должен импортировать браузерные API
- клиент не должен выполнять серверные модули
Для этого применяются:
- условные импорты (
import.meta.env.SSR)
- расширения
.server.js и .client.js
- плагинные хуки Vite SSR transform
Типичная проблема — “leaking code”, когда библиотека внутри
node_modules содержит браузерные вызовы.
Vite решает это через:
- dependency pre-bundling (optimizeDeps)
- externalization серверных зависимостей
- замены модулей через plugin hooks
SSR API и выполнение модулей
Основной механизм серверного выполнения модулей:
Он обеспечивает:
- загрузку ESM модулей в Node.js
- применение плагинов трансформации
- кэширование модулей
- корректное разрешение alias и paths
В отличие от стандартного import(), этот механизм
учитывает:
- vite plugin pipeline
- HMR boundary
- virtual modules
Кэширование и
производительность SSR
SSR в Vite тесно связан с эффективным кэшированием:
Кэш модулей
- серверные модули кешируются по пути и зависимости
- повторный рендер не требует повторной трансформации
HTTP caching
- HTML может кэшироваться на уровне CDN
- данные могут инвалидироваться отдельно
Prefetch чанков
- клиентские чанки заранее загружаются через
<link rel="modulepreload">
Это уменьшает задержки при гидрации.
SSR и плагины Vite
Плагины играют ключевую роль в SSR:
- трансформация JSX
- инъекция runtime
- обработка CSS modules
- поддержка Vue/React/Solid компиляторов
Каждый плагин может иметь SSR-специфичную логику:
transform() для server build
resolveId() с учетом SSR условий
load() с различным поведением
Это позволяет адаптировать один и тот же pipeline под разные среды
исполнения.
Ошибки архитектуры SSR в
Vite-проектах
Типовые проблемы:
Утечка браузерного кода на
сервер
- использование
window вне guards
- прямые обращения к DOM в setup-функциях
Несинхронный state hydration
- сервер и клиент используют разные источники данных
Дублирование запросов
- отсутствие shared cache между server/client fetching
Неправильная сборка чанков
- отсутствие корректного manifest mapping
Принцип минимального
рендера на сервере
SSR в Vite стремится к следующей модели:
- сервер выполняет только генерацию HTML и критических данных
- клиент выполняет всю интерактивность
- сервер не содержит бизнес-логики UI уровня
Это обеспечивает:
- предсказуемость рендера
- минимизацию нагрузки на Node.js
- ускорение TTFB
Архитектурная модель SSR в
Vite
В упрощённом виде SSR pipeline выглядит так:
- запрос → Node SSR сервер
- загрузка server entry через ssrLoadModule
- получение данных (если есть)
- рендер компонентов в HTML string/stream
- сопоставление с manifest
- отправка HTML + scripts
- клиентская гидрация
- восстановление состояния и запуск SPA-логики