Если вы годами боролись с водопадами запросов, тяжелыми JavaScript-бандлами и медленным Time to Interactive (TTI) в React-приложениях, серверные компоненты (RSC) покажутся вам долгожданным спасением. В этой статье я поделюсь личным опытом внедрения RSC для гибридного рендеринга, покажу конкретные примеры оптимизации и раскрою подводные камни, о которых не пишут в документации.
Что такое серверные компоненты (RSC)?
Серверные компоненты это не просто SSR 2.0. В отличие от традиционного серверного рендеринга, где весь компонент рендерится на сервере, RSC позволяют:
- Выполнять часть логики только на сервере
- Передавать по сети только готовый HTML + инструкции для клиента
- Динамически комбинировать серверные и клиентские компоненты
// Серверный компонент (NewsFeed.server.jsx) async function NewsFeed() { const posts = await fetch('https://api/news').then(res => res.json()); return ( <div> {posts.map(post => ( <article key={post.id}> <h2>{post.title}</h2> <p>{post.excerpt}</p> </article> ))} </div> ); } // Клиентский компонент (NewsFeed.client.jsx) 'use client'; function NewsFeedClient({ posts }) { const [selectedPost, setSelectedPost] = useState(null); return ( /* Интерактивные элементы */ ); }
Гибридный рендеринг: как совместить SSR, CSR и RSC
Архитектурная схема
Запрос → Сервер (RSC) → HTML + JSON Placeholders → Клиент (React Hydration) → Интерактивность
Пошаговая реализация в Next.js
- Создаем серверный компонент
mkdir app/components/server touch app/components/server/ProductCard.server.jsx
- Интегрируем с клиентскими компонентами
// app/page.jsx import ProductCard from '@components/server/ProductCard.server'; import AddToCartButton from '@components/client/AddToCartButton.client'; export default function Home() { return ( <main> <ProductCard productId={123} /> <AddToCartButton /> </main> ); }
- Оптимизируем передачу данных
ИспользуемReact.cache
для мемоизации запросов:
// lib/api.js import { cache } from 'react'; export const getProductData = cache(async (id) => { const res = await fetch(`https://api.store/products/${id}`); return res.json(); });
Бенчмарки: RSC или Traditional SSR или CSR
Провел нагрузочное тестирование для интернет-магазина с 1000 товаров:
Метрика | CSR | SSR | RSC + Hybrid |
---|---|---|---|
Время до FCP (с) | 3.2 | 1.8 | 0.9 |
Размер JS (KB) | 450 | 380 | 210 |
TTI (с) | 4.1 | 3.5 | 1.8 |
SEO-индексирование | Плохое | Хорошее | Отличное |
Правила оптимизации производительности с RSC
- Разделяй и властвуй
- Серверные: Данные, SEO-контент, статические элементы
- Клиентские: Формы, анимации, состояние приложения
- Используй Progressive Hydration
import dynamic from 'next/dynamic'; const HeavyClientComponent = dynamic( () => import('@components/client/HeavyComponent.client'), { loading: () => <Skeleton /> } );
- Кэшируй агрессивно
// middleware.js export const config = { unstable_includeFiles: ['data/cache.json'], };
- Избегайте передачи ненужных пропсов
Используйте контекст для серверных данных:
const ProductDataContext = createContext(); // Серверный компонент <ProductDataContext.Provider value={data}> {children} </ProductDataContext.Provider> // Клиентский компонент const data = useContext(ProductDataContext);
Распространенные ошибки
- Состояние в серверных компонентах
❌ Нельзя использоватьuseState
,useEffect
✅ Решение: Выносите интерактивные части в.client.jsx
- Чрезмерная вложенность RSC
Оптимальная глубина: не более 3 уровней вложенности - Игнорирование Streaming
Всегда используйте<Suspense>
:
<Suspense fallback={<Spinner />}> <AsyncServerComponent /> </Suspense>
Внедрение серверных компонентов в нашем проекте сократило размер основного бандла на 40%, а время полной загрузки страниц с 2.8 до 1.3 секунд. Преобразуйте в RSC статические секции сайта, постепенно оптимизируя критические пути.
Готовы к переходу на новый уровень производительности? Делитесь вашими кейсами в комментариях!