React Server Components позволяют рендерить часть UI на сервере, экономя клиентские ресурсы и ускоряя загрузку. В этой статье мы разберём, как включить их в проект, настроить Vite‑build и серверную отдачу через Express, чтобы получить полноценный SSR и Hydration без лишних настроек.

Разбор задачи

Нам нужно собрать приложение, где часть компонентов рендерится на сервере, а остальные – на клиенте. При этом Express будет отдавать готовый HTML с предварительно отрендеренными Server Components, а клиентский бандл будет выполнять Hydration. Используем Vite как сборщик, так как он быстро компилирует и поддерживает HMR, а также имеет плагины для SSR.

Пошаговое решение

В начале создаём простую структуру проекта: src/server для серверных компонентов, src/client для клиентских, и конфигурацию Vite. Далее в Vite добавляем плагин @vitejs/plugin-react, включаем режим SSR и задаём точку входа для Express. Конфиг Express настроим так, чтобы он принимал запросы, генерировал HTML через renderToString, и отдавал клиентский бандл. После запуска проекта вы увидите, что серверные компоненты уже рендерятся на стороне сервера, а клиентский скрипт вызывает Hydration, чтобы компоненты стали интерактивными.

Рабочий код

// server.js – Express сервер, который отдаёт готовый bundle
const express = require('express');
const path = require('path');
const fs = require('fs');
const app = express();
const port = process.env.PORT || 3000;

// Путь к скомпилированным файлам Vite
const distPath = path.resolve(__dirname, 'dist');

// Статический контент
app.use(express.static(distPath));

// Роут для SSR
app.get('*', async (req, res) => {
  try {
    // Импортируем серверный компонент, который будет использовать React Server Components
    const { renderToString } = await import('./dist/server.js');
    const html = await renderToString({ url: req.url });
    // Чтение шаблона index.html, в который вставляем сгенерированный HTML
    const template = fs.readFileSync(path.join(distPath, 'index.html'), 'utf-8');
    const finalHtml = template.replace('', html);
    res.send(finalHtml);
  } catch (e) {
    console.error(e);
    res.status(500).send('Error rendering page');
  }
});

app.listen(port, () => {
  console.log(`Server listening on http://localhost:${port}`);
});
// vite.config.js – конфигурация Vite для React Server Components
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [react({ ssr: true })],
  build: {
    ssr: 'src/entry-server.jsx',
    rollupOptions: {
      input: {
        main: 'src/entry-client.jsx',
        server: 'src/entry-server.jsx',
      },
    },
  },
});
// src/entry-server.jsx – точка входа для SSR
import React from 'react';
import { renderToString } from 'react-dom/server';
import App from './App.jsx';

export async function renderToString({ url }) {
  const context = { url };
  const appHtml = renderToString();
  return appHtml;
}
// src/entry-client.jsx – точка входа для клиентского гидратации
import React from 'react';
import { hydrateRoot } from 'react-dom/client';
import App from './App.jsx';

hydrateRoot(document.getElementById('root'), );

Типичные ошибки

  • Не включён флаг ssr: true в плагине React – без него Vite не генерирует серверный bundle.
  • Отсутствие renderToString в серверном entry‑point – React не умеет рендерить без него.
  • Неправильный путь к index.html в Express – приводит к 404.
  • Проблемы с кодом, использующим window или document – они не доступны в Node, нужно вынести в useEffect.

FAQ

Как отключить hydration, если я хочу только SSR?

В клиентском entry‑point замените hydrateRoot на createRoot и не вызывайте hydration, но тогда клиент не будет способен обновлять компоненты.

Можно ли использовать TypeScript?

Да, просто переименуйте файлы в .tsx и настройте tsconfig.json. Vite автоматически обработает TS.

Что делать, если сервер падает после обновления Vite?

Проверьте, что entry-server.jsx экспортирует renderToString и что Express импортирует его корректно. Часто проблема в кешировании модулей.

Заключение

Ниже чек‑лист, чтобы убедиться, что всё настроено правильно:

  • Vite настроен с ssr и правильными входами.
  • Express отдаёт статический bundle и корректно обрабатывает SSR.
  • Серверный и клиентский entry‑points экспортируют нужные функции.
  • Используются renderToString и hydrateRoot соответственно.
  • Отсутствуют обращения к window в серверном коде.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *