\n \n \n \n
\n

My Post

\n

Content here

\n
\n \n \n \n\n```\n\n**Шаг 3: Гидратация (Hydration) на клиенте**\n```javascript\n// React на клиенте получает:\n// 1. Уже готовый DOM (статический HTML)\n// 2. Данные из window.__NEXT_DATA__\n// 3. Компонент для рендера\n\n// React::\n// 1. Рендерит компонент в память (Virtual DOM)\n// 2. Сравнивает Virtual DOM с реальным DOM в браузере\n// 3. Если совпадают - отлично, добавляет обработчики событий\n// 4. Если не совпадают - ошибка гидратации\n```\n\n### Практический пример: Blog\n\n```typescript\n// app/blog/[slug]/page.tsx\nimport { Metadata } from 'next';\n\ninterface Post {\n id: string;\n slug: string;\n title: string;\n content: string;\n createdAt: string;\n}\n\n// Генерируется на сервере\nexport async function generateMetadata({ params }): Promise {\n const post = await fetchPost(params.slug);\n return { title: post.title, description: post.content };\n}\n\nexport default async function BlogPostPage({ params }) {\n // Серверная функция - данные получены на сервере\n const post: Post = await fetch(\n `https://api.example.com/posts/${params.slug}`\n ).then(r => r.json());\n\n return (\n
\n

{post.title}

\n \n
{post.content}
\n {/* Client Component для интерактивности */}\n \n
\n );\n}\n\n// app/blog/[slug]/PostActions.tsx\n'use client';\n\nimport { useState } from 'react';\n\nexport function PostActions({ postId }: { postId: string }) {\n const [liked, setLiked] = useState(false);\n\n return (\n \n );\n}\n```\n\n**Процесс формирования дерева:**\n\n```\n1. Сервер генерирует HTML:\n
\n

My Post

\n \n
Content here
\n \n
\n\n2. React на клиенте создаёт Virtual DOM:\n React.Element {\n type: 'article',\n props: {},\n children: [\n { type: 'h1', props: {}, children: 'My Post' },\n { type: 'time', props: {}, children: '2024-01-15' },\n { type: 'div', props: {}, children: 'Content here' },\n {\n type: PostActions,\n props: { postId: '123' },\n children: { type: 'button', ... }\n }\n ]\n }\n\n3. React сравнивает Virtual DOM с реальным DOM в браузере\n - HTML совпадает -> не пересоздаёт DOM\n - Добавляет обработчики событий\n - Компонент готов к взаимодействию\n```\n\n### Ошибка гидратации (Hydration Mismatch)\n\n```typescript\n// ❌ Это вызовет ошибку гидратации\nexport default function Component() {\n const [text, setText] = useState('');\n \n // Проблема: на сервере не будет вызван useEffect\n // На клиенте текст в первый момент пустой\n useEffect(() => {\n setText(new Date().toLocaleString());\n }, []);\n\n return
{text}
; // На сервере пусто, на клиенте текст\n}\n\n// Правильный подход:\nexport default function Component() {\n const [mounted, setMounted] = useState(false);\n\n useEffect(() => {\n setMounted(true);\n }, []);\n\n if (!mounted) return null; // На сервере и клиенте одинаково\n\n return
{new Date().toLocaleString()}
;\n}\n```\n\n### Server Components vs Client Components\n\n**Server Component (по умолчанию)**\n```typescript\n// Данные получены на СЕРВЕРЕ\nexport default async function UserProfile({ userId }) {\n const user = await db.user.findById(userId); // Прямой доступ к БД\n \n return (\n
\n

{user.name}

{/* HTML уже с данными */}\n \n
\n );\n}\n```\n\n**Client Component**\n```typescript\n'use client';\n\nimport { useState, useEffect } from 'react';\n\n// Данные получены на КЛИЕНТЕ\nexport function ClientComponent() {\n const [data, setData] = useState(null);\n\n useEffect(() => {\n fetch('/api/data').then(r => r.json()).then(setData);\n }, []);\n\n return
{data?.value}
; // Virtual DOM формируется в браузере\n}\n```\n\n### Процесс Virtual DOM в Next.js\n\n```\nServer Side (Node.js):\n┌─────────────────────────────┐\n│ getServerSideProps (данные) │\n└────────────┬────────────────┘\n │\n ▼\n┌─────────────────────────────┐\n│ Рендер React компонента │\n│ (создаёт Virtual DOM) │\n└────────────┬────────────────┘\n │\n ▼\n┌─────────────────────────────┐\n│ Преобразование в HTML │\n│ (renderToString) │\n└────────────┬────────────────┘\n │\n (отправить)\n │\n ▼\nClient Side (браузер):\n┌─────────────────────────────┐\n│ Получить HTML + данные │\n└────────────┬────────────────┘\n │\n ▼\n┌─────────────────────────────┐\n│ React гидратирует компонент │\n│ (создаёт Virtual DOM) │\n└────────────┬────────────────┘\n │\n ▼\n┌─────────────────────────────┐\n│ Сравнить Virtual DOM с DOM │\n│ Добавить обработчики │\n└─────────────────────────────┘\n```\n\n### На собеседовании\n\nХороший ответ включает:\n- Что данные получены на сервере в Server Components\n- Как Next.js генерирует HTML с данными\n- Процесс гидратации на клиенте\n- Virtual DOM и как React сравнивает его с реальным DOM\n- Различие между Server и Client Components\n- Как избежать ошибок гидратации\n\nЭто демонстрирует понимание архитектуры Next.js и SSR.","dateCreated":"2026-04-02T22:22:55.435446","upvoteCount":0,"author":{"@type":"Person","name":"claude-haiku-4.5"}}}}
← Назад к вопросам

Как виртуальное дерево формируется по данным Next.js полученным с сервера?

1.7 Middle🔥 191 комментариев
#JavaScript Core#Браузер и сетевые технологии

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

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

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

Как Next.js формирует виртуальное дерево из данных сервера

Этот вопрос проверяет понимание SSR (Server-Side Rendering), гидратации (hydration) и работы React Virtual DOM в контексте Next.js.

Жизненный цикл Next.js приложения

Шаг 1: Серверная генерация HTML

// app/posts/[id]/page.tsx - Server Component по умолчанию
export default async function PostPage({ params }) {
  const post = await fetch(`/api/posts/${params.id}`).then(r => r.json());
  
  return (
    <article>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
    </article>
  );
}

// Next.js на сервере:
// 1. Получает данные (post с id=1)
// 2. Рендерит компонент с этими данными
// 3. Генерирует HTML:
//    <article><h1>My Post</h1><p>Content here</p></article>

Шаг 2: Отправка на клиент

<!-- Браузер получает готовый HTML -->
<!DOCTYPE html>
<html>
  <head>
    <script src="/next/bundle.js"></script>
  </head>
  <body>
    <!-- Статический HTML с данными -->
    <article>
      <h1>My Post</h1>
      <p>Content here</p>
    </article>
    <!-- Специальный скрипт для гидратации -->
    <script>
      window.__NEXT_DATA__ = {
        props: { pageProps: { post: { id: 1, title: 'My Post', content: 'Content here' } } },
        page: '/posts/[id]'
      };
    </script>
  </body>
</html>

Шаг 3: Гидратация (Hydration) на клиенте

// React на клиенте получает:
// 1. Уже готовый DOM (статический HTML)
// 2. Данные из window.__NEXT_DATA__
// 3. Компонент для рендера

// React::
// 1. Рендерит компонент в память (Virtual DOM)
// 2. Сравнивает Virtual DOM с реальным DOM в браузере
// 3. Если совпадают - отлично, добавляет обработчики событий
// 4. Если не совпадают - ошибка гидратации

Практический пример: Blog

// app/blog/[slug]/page.tsx
import { Metadata } from 'next';

interface Post {
  id: string;
  slug: string;
  title: string;
  content: string;
  createdAt: string;
}

// Генерируется на сервере
export async function generateMetadata({ params }): Promise<Metadata> {
  const post = await fetchPost(params.slug);
  return { title: post.title, description: post.content };
}

export default async function BlogPostPage({ params }) {
  // Серверная функция - данные получены на сервере
  const post: Post = await fetch(
    `https://api.example.com/posts/${params.slug}`
  ).then(r => r.json());

  return (
    <article>
      <h1>{post.title}</h1>
      <time>{new Date(post.createdAt).toLocaleDateString()}</time>
      <div>{post.content}</div>
      {/* Client Component для интерактивности */}
      <PostActions postId={post.id} />
    </article>
  );
}

// app/blog/[slug]/PostActions.tsx
'use client';

import { useState } from 'react';

export function PostActions({ postId }: { postId: string }) {
  const [liked, setLiked] = useState(false);

  return (
    <button onClick={() => setLiked(!liked)}>
      {liked ? 'Liked' : 'Like'}
    </button>
  );
}

Процесс формирования дерева:

1. Сервер генерирует HTML:
   <article>
     <h1>My Post</h1>
     <time>2024-01-15</time>
     <div>Content here</div>
     <button>Like</button>
   </article>

2. React на клиенте создаёт Virtual DOM:
   React.Element {
     type: 'article',
     props: {},
     children: [
       { type: 'h1', props: {}, children: 'My Post' },
       { type: 'time', props: {}, children: '2024-01-15' },
       { type: 'div', props: {}, children: 'Content here' },
       {
         type: PostActions,
         props: { postId: '123' },
         children: { type: 'button', ... }
       }
     ]
   }

3. React сравнивает Virtual DOM с реальным DOM в браузере
   - HTML совпадает -> не пересоздаёт DOM
   - Добавляет обработчики событий
   - Компонент готов к взаимодействию

Ошибка гидратации (Hydration Mismatch)

// ❌ Это вызовет ошибку гидратации
export default function Component() {
  const [text, setText] = useState('');
  
  // Проблема: на сервере не будет вызван useEffect
  // На клиенте текст в первый момент пустой
  useEffect(() => {
    setText(new Date().toLocaleString());
  }, []);

  return <div>{text}</div>;  // На сервере пусто, на клиенте текст
}

// Правильный подход:
export default function Component() {
  const [mounted, setMounted] = useState(false);

  useEffect(() => {
    setMounted(true);
  }, []);

  if (!mounted) return null;  // На сервере и клиенте одинаково

  return <div>{new Date().toLocaleString()}</div>;
}

Server Components vs Client Components

Server Component (по умолчанию)

// Данные получены на СЕРВЕРЕ
export default async function UserProfile({ userId }) {
  const user = await db.user.findById(userId);  // Прямой доступ к БД
  
  return (
    <div>
      <h1>{user.name}</h1>  {/* HTML уже с данными */}
      <ClientInteractivity />
    </div>
  );
}

Client Component

'use client';

import { useState, useEffect } from 'react';

// Данные получены на КЛИЕНТЕ
export function ClientComponent() {
  const [data, setData] = useState(null);

  useEffect(() => {
    fetch('/api/data').then(r => r.json()).then(setData);
  }, []);

  return <div>{data?.value}</div>;  // Virtual DOM формируется в браузере
}

Процесс Virtual DOM в Next.js

Server Side (Node.js):
┌─────────────────────────────┐
│ getServerSideProps (данные) │
└────────────┬────────────────┘
             │
             ▼
┌─────────────────────────────┐
│ Рендер React компонента     │
│ (создаёт Virtual DOM)       │
└────────────┬────────────────┘
             │
             ▼
┌─────────────────────────────┐
│ Преобразование в HTML       │
│ (renderToString)            │
└────────────┬────────────────┘
             │
          (отправить)
             │
             ▼
Client Side (браузер):
┌─────────────────────────────┐
│ Получить HTML + данные      │
└────────────┬────────────────┘
             │
             ▼
┌─────────────────────────────┐
│ React гидратирует компонент │
│ (создаёт Virtual DOM)       │
└────────────┬────────────────┘
             │
             ▼
┌─────────────────────────────┐
│ Сравнить Virtual DOM с DOM  │
│ Добавить обработчики        │
└─────────────────────────────┘

На собеседовании

Хороший ответ включает:

  • Что данные получены на сервере в Server Components
  • Как Next.js генерирует HTML с данными
  • Процесс гидратации на клиенте
  • Virtual DOM и как React сравнивает его с реальным DOM
  • Различие между Server и Client Components
  • Как избежать ошибок гидратации

Это демонстрирует понимание архитектуры Next.js и SSR.