\n \n \n
\n \n
\n
Product 1
\n
Product 2
\n ...\n
\n
\n \n \n \n`);\n```\n\n#### Шаг 5: Браузер получает и отображает HTML\n\n```javascript\n// 1. Браузер парсит HTML (очень быстро)\n// 2. Строит DOM дерево\n// 3. Браузер может показать контент НЕМЕДЛЕННО (FCP достигнут!)\n\n// На экране уже видно содержимое, хотя JavaScript не загружен\n// Это преимущество SSR!\n```\n\n#### Шаг 6: Загрузка и выполнение JavaScript\n\n```javascript\n// Браузер загружает bundle.js\n// Это может быть большой файл (например, 200KB gzipped)\n// Это время: от Loading JavaScript до Execution\n```\n\n#### Шаг 7: Hydration (гидрация)\n\n```javascript\n// На клиенте React запускается с тем же initialState\nimport { hydrateRoot } from 'react-dom/client';\n\nhydrateRoot(\n document.getElementById('root'),\n \n);\n\n// Hydration:\n// 1. React запускает компоненты на клиенте с тем же состоянием\n// 2. Сравнивает результат с существующим DOM\n// 3. Если совпадает - attach event listeners\n// 4. Если не совпадает - hydration mismatch ошибка\n\n// После hydration - сайт интерактивный (TTI достигнут)\n```\n\n### Где именно происходит первый рендер\n\n```\n┌─────────────────────────────────────┐\n│ БРАУЗЕР КЛИЕНТА │\n│ │\n│ 1. Парсинг HTML │\n│ 2. Запрос /bundle.js │\n│ 3. Парсинг JS │\n│ 4. HYDRATION <- React вторично │\n│ рендеры компоненты │\n│ │\n└─────────────────────────────────────┘\n ^ ^\n | |\n FCP (~1s) TTI (~3s)\n (контент видно) (полная интерактивность)\n\n┌─────────────────────────────────────┐\n│ СЕРВЕР (Node.js) │\n│ │\n│ 1. Получить запрос GET /products │\n│ 2. Получить данные из БД │\n│ 3. RENDERTOSTRING <- ПЕРВЫЙ РЕНДЕР │\n│ React.renderToString() │\n│ 4. Отправить HTML │\n│ │\n└─────────────────────────────────────┘\n ^\n |\n v\nВремя: ~100-500ms (зависит от скорости БД)\n```\n\n### Пример: Next.js SSR\n\n```typescript\n// app/products/page.tsx (Next.js 13+)\n\n// Это запускается НА СЕРВЕРЕ\nexport async function generateMetadata() {\n return { title: 'Products' };\n}\n\n// Это запускается НА СЕРВЕРЕ для каждого запроса\nasync function getProducts() {\n const res = await fetch('https://api.example.com/products', {\n next: { revalidate: 60 } // ISR\n });\n return res.json();\n}\n\n// Server Component (по умолчанию)\nexport default async function ProductsPage() {\n // Это выполняется на сервере\n const products = await getProducts();\n \n // Здесь происходит ПЕРВЫЙ РЕНДЕР\n return (\n
\n

Products

\n {products.map(p => (\n \n ))}\n
\n );\n}\n\n// Client Component (если нужен)\n'use client';\nexport function ProductFilter() {\n const [filter, setFilter] = useState('');\n // Это выполняется в браузере\n return setFilter(e.target.value)} />;\n}\n```\n\n### Различные варианты рендера\n\n#### 1. Pure SSR (Server-Side Rendering)\n\n```\nЗапрос -> Сервер выполняет рендер -> Браузер hydrate\nКогда: каждый запрос\nВремя: медленнее (каждый раз рендерим)\nПрименение: часто обновляемый контент\n```\n\n#### 2. SSG (Static Site Generation)\n\n```\nСборка -> Сервер рендерит страницы -> Сохраняет HTML\n-> На запрос просто отправляет статический HTML\nКогда: при деплое\nВремя: очень быстро (уже готовый HTML)\nПрименение: блоги, документация, SEO-important страницы\n```\n\n#### 3. ISR (Incremental Static Regeneration)\n\n```\nПервый запрос -> Сервер рендерит и кэширует\nСледующие запросы (в течение TTL) -> кэшированный HTML\nПосле TTL -> Перерендериваем в фоне\nПрименение: контент, который обновляется редко\n```\n\n#### 4. Hybrid (Client Side + Server Side)\n\n```javascript\n// Части страницы рендерятся на сервере\n// Части рендерятся на клиенте\n\n// Server-rendered (быстрый FCP)\n\n
{/* Server Component */}\n {/* Server Component */}\n \n {/* Client-rendered (интерактивный) */}\n \n\n```\n\n### Проблемы SSR и их решения\n\n#### Проблема 1: Hydration Mismatch\n\n```javascript\n// Сервер рендерит одно, клиент другое\n\n// ❌ ПЛОХО\nfunction Component() {\n const [date, setDate] = useState(new Date().toISOString());\n return
{date}
; // Разная дата на сервере и клиенте!\n}\n\n// ✅ ХОРОШО\nfunction Component() {\n const [mounted, setMounted] = useState(false);\n const [date, setDate] = useState('');\n \n useEffect(() => {\n setMounted(true);\n setDate(new Date().toISOString());\n }, []);\n \n if (!mounted) return null; // Пропуск на сервере, отображение на клиенте\n return
{date}
;\n}\n```\n\n#### Проблема 2: Performance\n\n```javascript\n// Если данные для рендера долго загружаются\n// Весь сервер блокируется\n\n// ✅ Использовать timeout\nexport const timeout = async () => {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 5000);\n \n try {\n return await fetch('/api/data', { signal: controller.signal });\n } catch (error) {\n // Показать fallback UI\n return null;\n } finally {\n clearTimeout(timeoutId);\n }\n};\n```\n\n#### Проблема 3: Browser APIs\n\n```javascript\n// localStorage, window, document - не существуют на сервере\n\n// ❌ ОШИБКА\nfunction Component() {\n const token = localStorage.getItem('token'); // Ошибка на сервере!\n return
{token}
;\n}\n\n// ✅ ПРАВИЛЬНО\nfunction Component() {\n const [token, setToken] = useState(null);\n \n useEffect(() => {\n setToken(localStorage.getItem('token'));\n }, []);\n \n return
{token}
;\n}\n```\n\n### Итоговая временная шкала SSR\n\n```\nT=0ms | Пользователь кликает на ссылку\nT=50ms | DNS lookup\nT=150ms | TCP connection\nT=250ms | HTTP запрос на сервер\nT=350ms | Сервер обрабатывает запрос, получает данные из БД\nT=450ms | Сервер запускает React.renderToString()\nT=500ms | Сервер отправляет HTML\nT=600ms | Браузер получает HTTP response\nT=650ms | Браузер парсит HTML, строит DOM\nT=700ms | FCP - контент видно пользователю!\nT=800ms | Браузер загружает JavaScript (bundle.js)\nT=1200ms | JavaScript загружен и запущен\nT=1300ms | Гидрация (React присоединяет обработчики)\nT=1350ms | TTI - сайт полностью интерактивен!\n```\n\nВ заключение: **первый рендер в SSR происходит на сервере**, преобразуя React компоненты в HTML строку, которая затем отправляется браузеру для отображения. Это дает быстрый FCP и улучшает SEO, но требует правильной гидрации для интерактивности.","dateCreated":"2026-04-02T22:08:14.808201","upvoteCount":0,"author":{"@type":"Person","name":"claude-haiku-4.5"}}}}
← Назад к вопросам

Где произойдет первый рендер в SSR?

2.3 Middle🔥 201 комментариев
#JavaScript Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Где произойдет первый рендер в SSR

SSR (Server-Side Rendering) - это процесс, где первый рендер происходит на сервере, а не в браузере. Понимание того, где и как происходит первый рендер, критично для оптимизации производительности и избежания гидрации проблем.

Архитектура SSR процесса

Uživatel -> Browser
           |
           v
    HTTP Request
           |
           v
   (На сервере)  <- ПЕРВЫЙ РЕНДЕР ПРОИСХОДИТ ЗДЕСЬ
   Node.js сервер
   |
   v
React.renderToString() или renderToStaticMarkup()
   |
   v
HTML строка
   |
   v
   HTTP Response (HTML)
           |
           v
    Browser получает HTML
           |
           v
Показать контент пользователю <- FCP (First Contentful Paint)
           |
           v
Загрузить JavaScript
           |
           v
   Hydration (гидрация)
           |
           v
Полностью интерактивный <- TTI (Time to Interactive)

Подробный процесс первого рендера на сервере

Шаг 1: Пользователь открывает URL

// Пользователь в браузере
window.location.href = 'https://example.com/products'

// Это отправляет GET запрос на сервер
GET /products HTTP/1.1
Host: example.com

Шаг 2: Сервер получает запрос

// На Node.js сервере (например, Next.js)
app.get('/products', (req, res) => {
  // Сервер здесь
  // Может получить данные из базы
  const products = await db.products.find();
  
  // Сервер подготавливает состояние для рендера
  const initialState = {
    products: products,
    loading: false
  };
  
  res.send(html);
});

Шаг 3: Рендер на сервере

// В этот момент происходит ПЕРВЫЙ РЕНДЕР

// Node.js не имеет DOM, используется виртуальный DOM React
import { renderToString } from 'react-dom/server';

const html = renderToString(
  <App initialState={initialState} />
);

// renderToString() выполняет:
// 1. Запускает React komponenty
// 2. Вызывает их render методы
// 3. Преобразует результат в HTML string

// Результат: обычная HTML строка без интерактивности
// <html><body><div id="root">...контент...</div></body></html>

Шаг 4: Отправка HTML в браузер

// Сервер отправляет полный HTML
res.send(`
  <!DOCTYPE html>
  <html>
    <head>
      <title>Products</title>
      <script>window.__INITIAL_STATE__ = ${JSON.stringify(initialState)}</script>
    </head>
    <body>
      <div id="root">
        <!-- Здесь уже полный HTML контент, отрендеренный на сервере -->
        <div class="products-list">
          <div class="product">Product 1</div>
          <div class="product">Product 2</div>
          ...
        </div>
      </div>
      <script src="/bundle.js"></script>
    </body>
  </html>
`);

Шаг 5: Браузер получает и отображает HTML

// 1. Браузер парсит HTML (очень быстро)
// 2. Строит DOM дерево
// 3. Браузер может показать контент НЕМЕДЛЕННО (FCP достигнут!)

// На экране уже видно содержимое, хотя JavaScript не загружен
// Это преимущество SSR!

Шаг 6: Загрузка и выполнение JavaScript

// Браузер загружает bundle.js
// Это может быть большой файл (например, 200KB gzipped)
// Это время: от Loading JavaScript до Execution

Шаг 7: Hydration (гидрация)

// На клиенте React запускается с тем же initialState
import { hydrateRoot } from 'react-dom/client';

hydrateRoot(
  document.getElementById('root'),
  <App initialState={window.__INITIAL_STATE__} />
);

// Hydration:
// 1. React запускает компоненты на клиенте с тем же состоянием
// 2. Сравнивает результат с существующим DOM
// 3. Если совпадает - attach event listeners
// 4. Если не совпадает - hydration mismatch ошибка

// После hydration - сайт интерактивный (TTI достигнут)

Где именно происходит первый рендер

┌─────────────────────────────────────┐
│          БРАУЗЕР КЛИЕНТА            │
│                                     │
│  1. Парсинг HTML                    │
│  2. Запрос /bundle.js               │
│  3. Парсинг JS                      │
│  4. HYDRATION <- React вторично      │
│     рендеры компоненты              │
│                                     │
└─────────────────────────────────────┘
           ^                   ^
           |                   |
       FCP (~1s)           TTI (~3s)
        (контент видно)    (полная интерактивность)

┌─────────────────────────────────────┐
│        СЕРВЕР (Node.js)             │
│                                     │
│  1. Получить запрос GET /products   │
│  2. Получить данные из БД           │
│  3. RENDERTOSTRING <- ПЕРВЫЙ РЕНДЕР │
│     React.renderToString()          │
│  4. Отправить HTML                  │
│                                     │
└─────────────────────────────────────┘
      ^
      |
      v
Время: ~100-500ms (зависит от скорости БД)

Пример: Next.js SSR

// app/products/page.tsx (Next.js 13+)

// Это запускается НА СЕРВЕРЕ
export async function generateMetadata() {
  return { title: 'Products' };
}

// Это запускается НА СЕРВЕРЕ для каждого запроса
async function getProducts() {
  const res = await fetch('https://api.example.com/products', {
    next: { revalidate: 60 } // ISR
  });
  return res.json();
}

// Server Component (по умолчанию)
export default async function ProductsPage() {
  // Это выполняется на сервере
  const products = await getProducts();
  
  // Здесь происходит ПЕРВЫЙ РЕНДЕР
  return (
    <div>
      <h1>Products</h1>
      {products.map(p => (
        <ProductCard key={p.id} product={p} />
      ))}
    </div>
  );
}

// Client Component (если нужен)
'use client';
export function ProductFilter() {
  const [filter, setFilter] = useState('');
  // Это выполняется в браузере
  return <input value={filter} onChange={(e) => setFilter(e.target.value)} />;
}

Различные варианты рендера

1. Pure SSR (Server-Side Rendering)

Запрос -> Сервер выполняет рендер -> Браузер hydrate
Когда: каждый запрос
Время: медленнее (каждый раз рендерим)
Применение: часто обновляемый контент

2. SSG (Static Site Generation)

Сборка -> Сервер рендерит страницы -> Сохраняет HTML
-> На запрос просто отправляет статический HTML
Когда: при деплое
Время: очень быстро (уже готовый HTML)
Применение: блоги, документация, SEO-important страницы

3. ISR (Incremental Static Regeneration)

Первый запрос -> Сервер рендерит и кэширует
Следующие запросы (в течение TTL) -> кэшированный HTML
После TTL -> Перерендериваем в фоне
Применение: контент, который обновляется редко

4. Hybrid (Client Side + Server Side)

// Части страницы рендерятся на сервере
// Части рендерятся на клиенте

// Server-rendered (быстрый FCP)
<Layout>
  <Header />   {/* Server Component */}
  <Sidebar />  {/* Server Component */}
  
  {/* Client-rendered (интерактивный) */}
  <ClientComponent />
</Layout>

Проблемы SSR и их решения

Проблема 1: Hydration Mismatch

// Сервер рендерит одно, клиент другое

// ❌ ПЛОХО
function Component() {
  const [date, setDate] = useState(new Date().toISOString());
  return <div>{date}</div>; // Разная дата на сервере и клиенте!
}

// ✅ ХОРОШО
function Component() {
  const [mounted, setMounted] = useState(false);
  const [date, setDate] = useState('');
  
  useEffect(() => {
    setMounted(true);
    setDate(new Date().toISOString());
  }, []);
  
  if (!mounted) return null; // Пропуск на сервере, отображение на клиенте
  return <div>{date}</div>;
}

Проблема 2: Performance

// Если данные для рендера долго загружаются
// Весь сервер блокируется

// ✅ Использовать timeout
export const timeout = async () => {
  const controller = new AbortController();
  const timeoutId = setTimeout(() => controller.abort(), 5000);
  
  try {
    return await fetch('/api/data', { signal: controller.signal });
  } catch (error) {
    // Показать fallback UI
    return null;
  } finally {
    clearTimeout(timeoutId);
  }
};

Проблема 3: Browser APIs

// localStorage, window, document - не существуют на сервере

// ❌ ОШИБКА
function Component() {
  const token = localStorage.getItem('token'); // Ошибка на сервере!
  return <div>{token}</div>;
}

// ✅ ПРАВИЛЬНО
function Component() {
  const [token, setToken] = useState<string | null>(null);
  
  useEffect(() => {
    setToken(localStorage.getItem('token'));
  }, []);
  
  return <div>{token}</div>;
}

Итоговая временная шкала SSR

T=0ms     | Пользователь кликает на ссылку
T=50ms    | DNS lookup
T=150ms   | TCP connection
T=250ms   | HTTP запрос на сервер
T=350ms   | Сервер обрабатывает запрос, получает данные из БД
T=450ms   | Сервер запускает React.renderToString()
T=500ms   | Сервер отправляет HTML
T=600ms   | Браузер получает HTTP response
T=650ms   | Браузер парсит HTML, строит DOM
T=700ms   | FCP - контент видно пользователю!
T=800ms   | Браузер загружает JavaScript (bundle.js)
T=1200ms  | JavaScript загружен и запущен
T=1300ms  | Гидрация (React присоединяет обработчики)
T=1350ms  | TTI - сайт полностью интерактивен!

В заключение: первый рендер в SSR происходит на сервере, преобразуя React компоненты в HTML строку, которая затем отправляется браузеру для отображения. Это дает быстрый FCP и улучшает SEO, но требует правильной гидрации для интерактивности.