Где рендерятся серверные компоненты?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Где рендерятся серверные компоненты?
Отличный вопрос о современной архитектуре фронтенда. Это ключевая особенность Next.js 13+ и паттерна Server Components.
Краткий ответ
Серверные компоненты рендерятся на сервере (Node.js процесс), результат отправляется клиенту как HTML и специальный React Server Component Payload.
Где это происходит
1. На сервере (Server-Side)
// app/page.jsx - это Server Component по умолчанию в Next.js 13+
import { db } from "@/lib/db";
import { ProductCard } from "@/components/ProductCard";
// Это выполнится на сервере
export default async function ProductsPage() {
// ✅ Прямой доступ к БД
const products = await db.query("SELECT * FROM products");
// ✅ Может использовать secrets
const apiKey = process.env.SECRET_API_KEY;
const data = await fetch("https://internal-api.com", {
headers: { Authorization: `Bearer ${apiKey}` }
});
return (
<div>
{products.map((product) => (
<ProductCard key={product.id} product={product} />
))}
</div>
);
}
Этот код НИКОГДА не попадет в бандл браузера. Он выполняется только на сервере.
Процесс рендеринга
Шаг 1: Сборка (Build time)
// next.config.js
module.exports = {
experimental: {
serverActions: true // Включаем Server Actions
}
};
Ди сборке Next.js:
- Анализирует какие компоненты — Server Components
- Какие — Client Components
- Генерирует оптимизированный bundle
Шаг 2: Runtime (Во время запроса)
1. Браузер отправляет запрос → GET /products
2. Next.js сервер получает запрос
3. Выполняет Server Component код
4. Запрашивает данные из БД
5. Рендерит React дерево на сервере
6. Отправляет результат браузеру
7. Браузер выводит HTML + гидрирует Client Components
Server Component vs Client Component
Server Component (по умолчанию)
// app/components/UserProfile.jsx
// Без "use client" — это Server Component
import { getUserData } from "@/lib/db";
export default async function UserProfile({ userId }) {
// Может быть async!
const user = await getUserData(userId);
return (
<div>
<h1>{user.name}</h1>
<p>Email: {user.email}</p>
</div>
);
}
Client Component
// app/components/InteractiveButton.jsx
"use client"; // Явное указание
import { useState } from "react";
export default function InteractiveButton() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
Clicked {count} times
</button>
);
}
Разница:
- Server Component → выполняется только на сервере
- Client Component → отправляется в браузер, выполняется там
Архитектура рендеринга
┌─────────────────────────────────────────┐
│ Client (Browser) │
│ - DOM │
│ - Event Listeners │
│ - Interactive UI │
└──────────────┬──────────────────────────┘
↑ HTTP (HTML + RSC Payload)
↓
┌──────────────────────────────────────────┐
│ Server (Node.js Runtime) │
│ - Database Access │
│ - Secret Keys │
│ - File System │
│ - Server Component Rendering │
└──────────────────────────────────────────┘
Практический пример
// app/page.jsx - Server Component
import { Suspense } from "react";
import { PostList } from "@/components/PostList";
import { PostSkeleton } from "@/components/PostSkeleton";
async function getPosts() {
// Выполняется на сервере
const response = await fetch("https://api.example.com/posts", {
headers: { "Authorization": `Bearer ${process.env.API_SECRET}` }
});
return response.json();
}
export default function Page() {
return (
<main>
<h1>Blog Posts</h1>
{/* Streaming: Suspense граница для postupных данных */}
<Suspense fallback={<PostSkeleton />}>
<PostList postsPromise={getPosts()} />
</Suspense>
</main>
);
}
React Server Component Payload
Это специальный формат, который отправляется браузеру:
// Что браузер получает после рендеринга Server Component
// Это не просто HTML, а специальный формат
M1:{"id":"./app/page.jsx","chunks":[],"name":"Page","async":false}
M2:{"id":"./components/Header.jsx","chunks":[],"name":"Header","async":false}
J0:["@1","span",null,{"children":"Hello World"}]
Этот payload позволяет браузеру:
- Гидрировать интерактивные компоненты
- Обновлять части страницы без полного перезагрузки
- Работать с React Query и другими state-менеджерами
Streaming и Suspense
Server Components поддерживают streaming — отправка контента частями:
// app/dashboard/page.jsx
import { Suspense } from "react";
import { UserStats } from "@/components/UserStats";
import { RecentActivity } from "@/components/RecentActivity";
async function getUserStats(userId) {
// Может быть медленно
await new Promise(resolve => setTimeout(resolve, 1000));
return { views: 1000, followers: 500 };
}
async function getActivity(userId) {
// Может быть быстро
return [{ id: 1, text: "Posted" }, { id: 2, text: "Liked" }];
}
export default function Dashboard({ params }) {
return (
<div>
{/* Эта часть загружается быстро */}
<Suspense fallback={<div>Loading activity...</div>}>
<RecentActivity activityPromise={getActivity(params.userId)} />
</Suspense>
{/* Эта часть может быть медленнее */}
<Suspense fallback={<div>Loading stats...</div>}>
<UserStats statsPromise={getUserStats(params.userId)} />
</Suspense>
</div>
);
}
Преимущества Server Components
1. Безопасность
// Secrets не попадают в браузер
const password = process.env.DB_PASSWORD; // ✅ Только на сервере
2. Производительность
// Нет JavaScript в браузере для статичного контента
// Размер бандла меньше
3. Прямой доступ к БД
// Нет лишних API запросов
const data = await db.query("SELECT...");
4. Streaming
// Отправка контента по частям
// Улучшена Core Web Vitals
Когда использовать Server Components
- Запросы к БД
- Скрытые API ключи
- Работа с файловой системой
- Крупные библиотеки (которые сокращают размер бандла)
- Аутентификация
Когда использовать Client Components
- Интерактивность (onClick, onChange, и т.д.)
- Хуки (useState, useEffect, useContext)
- Браузерный API (localStorage, sessionStorage)
- Слушание событий
Заключение
Серверные компоненты рендерятся на сервере (Node.js процесс), результат отправляется клиенту как HTML + React Server Component Payload. Это позволяет:
- Безопасно работать с секретами
- Уменьшить размер JavaScript бандла
- Улучшить производительность
- Использовать Suspense для streaming
Это один из главных паттернов современного фронтенда с Next.js 13+.