12. SSR с Vite
Vite предоставляет первоклассную поддержку Server-Side Rendering. В отличие от client-side только приложений, SSR рендерит HTML на сервере и отправляет уже готовую разметку браузеру.
Принцип работы SSR с Vite
Заголовок раздела «Принцип работы SSR с Vite»SSR запрос:1. Browser → GET /dashboard2. Express/Fastify → handler3. vite.ssrLoadModule('./src/entry-server.tsx') → рендер4. React.renderToString(<App />) → HTML строка5. Inject в index.html шаблон6. Response → полный HTML
Hydration:7. Browser получает HTML → отображает сразу8. Browser загружает JS бандл9. React "оживляет" (hydrates) существующий DOMСтруктура SSR проекта
Заголовок раздела «Структура SSR проекта»my-ssr-app/├── index.html ← Шаблон с <!--app-html--> placeholder├── src/│ ├── main.tsx ← Client entry (для hydration)│ ├── entry-server.tsx ← Server entry│ └── App.tsx├── server.js ← Express/Fastify сервер└── vite.config.tsServer Entry
Заголовок раздела «Server Entry»import { renderToString } from 'react-dom/server'import { StaticRouter } from 'react-router-dom/server'import { App } from './App'
export function render(url: string) { const html = renderToString( <StaticRouter location={url}> <App /> </StaticRouter> ) return { html }}Client Entry (Hydration)
Заголовок раздела «Client Entry (Hydration)»import { hydrateRoot } from 'react-dom/client'import { BrowserRouter } from 'react-router-dom'import { App } from './App'
hydrateRoot( document.getElementById('root')!, <BrowserRouter> <App /> </BrowserRouter>)Express сервер с Vite SSR
Заголовок раздела «Express сервер с Vite SSR»import express from 'express'import { createServer as createViteServer } from 'vite'
async function createServer() { const app = express()
// Dev режим: используем Vite middleware if (process.env.NODE_ENV !== 'production') { const vite = await createViteServer({ server: { middlewareMode: true }, appType: 'custom', }) app.use(vite.middlewares)
app.use('*', async (req, res, next) => { try { const url = req.originalUrl
// Читаем index.html шаблон let template = fs.readFileSync('index.html', 'utf-8') // Vite трансформирует шаблон (инжектирует HMR) template = await vite.transformIndexHtml(url, template)
// Загружаем server entry через Vite SSR const { render } = await vite.ssrLoadModule('/src/entry-server.tsx') const { html: appHtml } = await render(url)
// Вставляем рендер в шаблон const finalHtml = template.replace('<!--app-html-->', appHtml) res.status(200).set({ 'Content-Type': 'text/html' }).end(finalHtml) } catch (e) { vite.ssrFixStacktrace(e as Error) next(e) } }) }
app.listen(5173)}
createServer()vite.config.ts для SSR
Заголовок раздела «vite.config.ts для SSR»export default defineConfig({ plugins: [react()], build: { // Client build (по умолчанию) outDir: 'dist/client', },})
// Для server build:// vite build --ssr src/entry-server.tsx --outDir dist/serverStreaming SSR (React 18+)
Заголовок раздела «Streaming SSR (React 18+)»import { renderToPipeableStream } from 'react-dom/server'
export function render(url: string, res: Response) { const { pipe } = renderToPipeableStream( <App />, { onShellReady() { res.setHeader('content-type', 'text/html') pipe(res) }, onError(error) { console.error(error) }, } )}// HTML начинает отправляться до окончания всего рендера!Мета-фреймворки на базе Vite + SSR
Заголовок раздела «Мета-фреймворки на базе Vite + SSR»Vite SSR ← Базовый слой для:├── Nuxt 3 (Vue + SSR/SSG)├── SvelteKit (Svelte + SSR/SSG/SPA)├── Astro (Multi-framework SSR/SSG)├── Remix (Vite mode) (React + SSR)└── Qwik City (Qwik + SSR + Resumability)Ниже — визуализация SSR пайплайна: от HTTP запроса до отображения страницы в браузере: