Server Components: будущее фронтенда или очередной эксперiment?

Разбираемся с React Server Components простым языком: что это, зачем нужно и стоит ли изучать? Сравниваем с обычными компонентами, разбираем реальные примеры кода и выясняем, действительно ли это будущее фронтенда или очередной модный тренд.

WebРазработка

6 мин

Если вы следите за новостями в мире React, то наверняка слышали о Server Components — технологии, которая вызывает бурные обсуждения в сообществе. Одни называют её революцией во фронтенд-разработке, другие — излишним усложнением.

Что такое Server Components?

Server Components (серверные компоненты) — это новый тип React-компонентов, которые рендерятся исключительно на сервере и никогда не попадают в браузер. Звучит странно?

Давайте посмотрим на простой пример:

// ServerComponent.js (серверный компонент)
async function BlogPost({ id }) {
  // Этот код выполняется ТОЛЬКО на сервере
  const post = await db.posts.findById(id);
  
  return (
    <article>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
    </article>
  );
}

Обратите внимание: мы напрямую обращаемся к базе данных в компоненте! Раньше такое было невозможно — приходилось создавать API-эндпоинты, делать fetch-запросы, обрабатывать состояния загрузки... Теперь всё это можно делать прямо в компоненте.

Чем Server Components отличаются от обычных?

Давайте сравним три типа компонентов, чтобы понять разницу:

1. Client Components (клиентские компоненты)

Это обычные React-компоненты, которые вы знаете. Они:

  • Выполняются в браузере

  • Могут использовать хуки (useState, useEffect и т.д.)

  • Могут обрабатывать события (onClick, onChange)

  • Увеличивают размер JavaScript-бандла

'use client'; // Явно указываем, что это клиентский компонент

function Counter() {
  const [count, setCount] = useState(0);
  
  return (
    <button onClick={() => setCount(count + 1)}>
      Clicked {count} times
    </button>
  );
}

2. Server Components (серверные компоненты)

Новый тип компонентов, которые:

  • Рендерятся ТОЛЬКО на сервере

  • Не попадают в JavaScript-бандл

  • Могут напрямую работать с базами данных и файловой системой

  • НЕ могут использовать хуки состояния или браузерные API

  • НЕ могут обрабатывать события

// По умолчанию в Next.js 13+ все компоненты серверные
async function UserProfile({ userId }) {
  // Прямой доступ к БД — это код сервера!
  const user = await db.users.findById(userId);
  const posts = await db.posts.findByUser(userId);
  
  return (
    <div>
      <h2>{user.name}</h2>
      <p>{posts.length} постов</p>
    </div>
  );
}

3. Server-Side Rendering (SSR)

Не путайте Server Components с SSR! Это разные вещи:

  • SSR — рендерит HTML на сервере, но весь JavaScript всё равно загружается в браузер для "гидратации"

  • Server Components — вообще не отправляют свой JavaScript в браузер, только готовый результат

Зачем это нужно?

Проблема №1: Раздутые JavaScript-бандлы

Представьте: вы выводите список товаров с markdown-описаниями. Раньше приходилось включать библиотеку для парсинга markdown в клиентский бандл:

// Старый подход — библиотека попадёт в браузер
import { marked } from 'marked'; // ~50kb

function Product({ description }) {
  return <div dangerouslySetInnerHTML={{ __html: marked(description) }} />;
}

С Server Components библиотека остаётся на сервере:

// Новый подход — библиотека НЕ попадает в браузер
import { marked } from 'marked';

async function Product({ productId }) {
  const product = await db.products.findById(productId);
  const html = marked(product.description);
  
  return <div dangerouslySetInnerHTML={{ __html: html }} />;
}

Результат: браузер получает готовый HTML без лишних 50kb JavaScript!

Проблема №2: Водопад запросов

Классическая проблема React-приложений:

// Плохо: водопад запросов
function Dashboard() {
  const { user } = useUser(); // Запрос 1
  if (!user) return <Loader />;
  
  return <UserPosts userId={user.id} />; // Запрос 2 начнётся только после 1
}

С Server Components запросы выполняются параллельно на сервере:

async function Dashboard() {
  // Оба запроса выполнятся параллельно!
  const [user, posts] = await Promise.all([
    db.users.getCurrent(),
    db.posts.getRecent()
  ]);
  
  return (
    <div>
      <UserInfo user={user} />
      <PostsList posts={posts} />
    </div>
  );
}

Проблема №3: Безопасность

Server Components позволяют хранить секреты на сервере:

// Безопасно — API ключ никогда не попадёт в браузер
async function WeatherWidget({ city }) {
  const response = await fetch(
    `https://api.weather.com/data?key=${process.env.WEATHER_API_KEY}&city=${city}`
  );
  const data = await response.json();
  
  return <div>Температура: {data.temp}°C</div>;
}

Практический пример: блог-платформа.

Давайте посмотрим на реальное приложение, которое сочетает оба типа компонентов:

// app/posts/[id]/page.js - серверный компонент
async function PostPage({ params }) {
  // Данные загружаются на сервере
  const post = await db.posts.findById(params.id);
  const comments = await db.comments.findByPost(params.id);
  
  return (
    <article>
      <h1>{post.title}</h1>
      <PostContent content={post.content} />
      
      {/* Клиентский компонент для интерактивности */}
      <CommentForm postId={post.id} />
      
      {/* Серверный компонент для отображения */}
      <CommentsList comments={comments} />
    </article>
  );
}

// components/CommentForm.js - клиентский компонент
'use client';

function CommentForm({ postId }) {
  const [text, setText] = useState('');
  
  const handleSubmit = async (e) => {
    e.preventDefault();
    await fetch('/api/comments', {
      method: 'POST',
      body: JSON.stringify({ postId, text })
    });
    setText('');
  };
  
  return (
    <form onSubmit={handleSubmit}>
      <textarea 
        value={text} 
        onChange={(e) => setText(e.target.value)}
      />
      <button type="submit">Отправить</button>
    </form>
  );
}

Преимущества Server Components

✅ Меньше JavaScript в браузере

Только интерактивные части приложения загружаются в браузер. Всё остальное — на сервере.

✅ Прямой доступ к серверным ресурсам

База данных, файловая система, внутренние API — всё доступно напрямую из компонентов.

✅ Лучшая производительность

Данные загружаются близко к источнику (сервер → БД быстрее, чем браузер → API → БД).

✅ Автоматическое разделение кода

Вам не нужно думать о code splitting — серверные компоненты автоматически не попадают в бандл.

✅ Улучшенная безопасность

API ключи, токены, логика бизнес-процессов остаются на сервере.

Недостатки и сложности

❌ Крутая кривая обучения

Нужно понимать, какой компонент где выполняется. Новички часто путаются:

// ❌ Ошибка: Server Component не может использовать useState
async function UserProfile() {
  const [isOpen, setIsOpen] = useState(false); // Упс!
  // ...
}

// ✅ Правильно: выносим интерактивность в Client Component
async function UserProfile() {
  const user = await db.users.getCurrent();
  return <ProfileCard user={user} />; // ProfileCard может быть клиентским
}

❌ Ограниченная экосистема

На данный момент полноценная поддержка есть только в Next.js 13+. Другие фреймворки только начинают внедрять эту технологию.

❌ Сложности с отладкой

Когда часть кода выполняется на сервере, а часть в браузере, отладка становится сложнее.

❌ Больше нагрузка на сервер

Каждый запрос требует рендеринга на сервере. Нужно продумывать кеширование и масштабирование.

Когда использовать Server Components?

Идеально подходят для:

  • Страниц с динамическим контентом — блоги, новостные ленты, каталоги товаров

  • Дашбордов с большим количеством данных — аналитика, отчёты, статистика

  • SEO-критичных страниц — весь контент рендерится на сервере и доступен поисковикам

  • Приложений с тяжёлыми зависимостями — markdown, подсветка кода, обработка изображений

Не подходят для:

  • Высокоинтерактивных интерфейсов — редакторы, игры, рисовалки

  • Офлайн-приложений — PWA, которые работают без сервера

  • Приложений с real-time обновлениями — чаты, совместное редактирование

Как начать экспериментировать?

Самый простой способ попробовать Server Components — создать новый проект на Next.js 13+:

npx create-next-app@latest my-app
cd my-app
npm run dev

В Next.js 13+ с App Router все компоненты по умолчанию серверные. Чтобы сделать компонент клиентским, просто добавьте 'use client' в начало файла.

Практические советы.

1. Начинайте с серверных компонентов

Делайте компонент клиентским только когда это действительно нужно (состояние, события, браузерные API).

2. Используйте "client boundary"

Выносите интерактивность в отдельные маленькие компоненты:

// Серверный компонент
async function ProductPage({ id }) {
  const product = await db.products.findById(id);
  
  return (
    <div>
      <h1>{product.name}</h1>
      <p>{product.description}</p>
      
      {/* Только кнопка клиентская */}
      <AddToCartButton productId={product.id} />
    </div>
  );
}

3. Кешируйте данные

Next.js автоматически кеширует fetch-запросы, но вы можете управлять этим:

// Кеширование на 1 час
const data = await fetch('https://api.example.com/data', {
  next: { revalidate: 3600 }
});

Стоит ли изучать? Однозначно да, особенно если вы работаете с Next.js или планируете это делать. Но помните: хороший разработчик знает, когда использовать новую технологию, а когда придерживаться проверенных решений.

Server Components, хуки, производительность, архитектура приложений — это и многое другое можно изучить в Кодике! Мы разбираем темы подробно, от основ до продвинутых концепций, и закрепляем знания практическими заданиями.

💬 А если нужна поддержка или хочется обсудить код — у нас уже больше 2000 единомышленников в активном телеграм-канале, где помогают друг другу, делятся опытом и обсуждают актуальные технологии!

Присоединяйтесь к Кодику — учитесь эффективно, практикуйтесь регулярно, растите профессионально! 🎯

Удачи в освоении Server Components! Помните: лучший способ понять технологию — попробовать её на практике. Создайте небольшой проект и экспериментируйте! 💻

Комментарии