11 - server and client components decomposition

Для доступа к видео войдите в систему

11 - server and client components decomposition

Оценить качество материала и подачу материала автором видео:

Front-end

Трудоустройтесь middle front-end разработчиком на React JS (TypeScript) за 12-16 месяцев обучения с ежедневной менторской поддержкой в формате видео 1 на 1 и коммерческими проектами в портфолио

Перейти на курс
Front-end

Back-end

Трудоустройтесь middle back-end разработчиком за 12-16 месяцев обучения с ежедневной менторской поддержкой в формате видео 1 на 1 и коммерческими проектами в портфолио

Перейти на курс
Back-end

Карьерный бустер

Получите коммерческий опыт на реальных стартапах, прокачайте tech & soft навыки, научитесь работать в команде, проходить собеседования и получите первую работу в IT!

Перейти на курс
Карьерный бустер

Основы Front-end

Сделайте первый шаг в IT, освоив базовые знания разработки и научившись создавать небольшие проекты на JavaScript

Перейти на курс
Основы Front-end

Основы Back-end

Сделайте первый шаг в IT, освоив базовые знания разработки. Без опыта. Без математики. Только практика: JavaScript, SQL, Node JS, база данных

Перейти на курс
Основы Back-end

Server and client components decomposition

Автор конспекта: Стогниева Виктория

1️⃣ Введение: стратегия декомпозиции для оптимальной производительности

Правильное размещение клиентских и серверных компонентов в Next.js — это фундамент современной архитектуры приложений ⚙️ Грамотная декомпозиция — не просто вопрос структуры кода, а ключевой инструмент оптимизации производительности 🚀

🎯 Главная цель стратегии — максимально сохранить чистоту серверного рендеринга и отправлять в браузер только тот JavaScript, который действительно необходим для интерактивности.

2️⃣ Клиентский компонент внутри серверного

Принцип «минимальной необходимости» 🧩

Встраивание интерактивных элементов в страницы, которые по умолчанию являются серверными, — одна из самых частых задач в Next.js.

📌 Стратегическая важность Даже добавляя интерактивность, необходимо:

  • ⚡ сохранить высокую скорость загрузки,
  • 🔍 не потерять SEO-преимущества,
  • 📉 минимизировать клиентский JavaScript.

2.1 ❗ Описание проблемы: ошибка при использовании интерактивности

Проблема возникает, когда разработчик пытается разместить компонент с обработчиками событий (например, клики) напрямую внутри серверного компонента.

🛑 Почему это ошибка

  • Серверные компоненты выполняются только на сервере
  • Их JavaScript не отправляется в браузер
  • Пользовательские события требуют клиентской среды

👉 В результате возникает ошибка во время рендеринга.

MistakeinNext

2.2 ⚖️ Сравнение двух подходов к решению

Существует два способа устранить ошибку, но только один из них оптимален с точки зрения производительности.

✅ Правильный подход (оптимальный): изолированная клиентская директива

  • Интерактивность изолируется в отдельном компоненте
  • Клиентская директива применяется только к нему
  • Родительский компонент и остальная часть дерева остаются серверными

💡 Ключевое преимущество В браузер попадает только минимально необходимый JavaScript, что:

  • уменьшает размер бандла 📦
  • ускоряет загрузку страницы ⚡
  • сохраняет SEO 📈

❌ Неправильный подход (субоптимальный): избыточная клиентская директива

  • Весь родительский компонент помечается как клиентский
  • Вся вложенная структура становится клиентской

💥 Цена ошибки

  • В бандл попадает лишний JavaScript
  • Увеличивается время загрузки
  • Теряются преимущества серверного рендеринга
💡

Проблема усугубляется тем, что сообщение об ошибке часто указывает на родительский компонент, подталкивая к интуитивному, но неверному решению.

2.3 🎯 Ключевой принцип оптимизации

Главный вывод:

💡

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

📉 Это позволяет:

  • минимизировать размер JavaScript,
  • повысить производительность,
  • сохранить масштабируемость приложения.
header.tsx
import Link from 'next/link'
import styles from './header.module.css'
 
export const Header = () => (
  <nav className={styles.navigation}>
    <ul>
      <li>
        <Link href="/" className={styles.link}>Main</Link>
      </li>
      <li>
        <Link href="/profile" className={styles.link}>Profile</Link>
      </li>
      <li>
        <Link href="/books" className={styles.link}>Books</Link>
      </li>
    </ul>
    <Test/>
  </nav>
)
test.tsx
'use client'
 
export const Test = () => (
  <button onClick={() => console.log('Test Client')}>Log</button>
)

3️⃣ Серверный компонент внутри клиентского

Паттерн «композиция через props» 🧠

Размещение серверного компонента внутри клиентского — более сложная архитектурная задача.

❗ Директива клиентского компонента каскадно применяется ко всем дочерним элементам, что на первый взгляд делает невозможным использование серверной логики.

Однако существует элегантный архитектурный паттерн, рекомендованный Next.js.

3.1 🧱 Описание паттерна: передача через children

Решение строится на композиции, а не на прямом импорте.

🔹 Шаг 1. Клиентский компонент Он настраивается так, чтобы принимать и отображать дочерние элементы.

🔹 Шаг 2. Серверный компонент Создаётся отдельный серверный компонент, который:

  • инкапсулирует серверную логику,
  • отвечает за получение данных,
  • формирует статическую разметку.

🔹 Шаг 3. Композиция в родителе В родительском серверном компоненте происходит объединение клиентского «контейнера» и серверного «содержимого».

test-server.tsx
export async function TestServer() {
  const data = await fetch('https://jsonplaceholder.typicode.com/todos/1')
  const todoList = await data.json()
 
  return (
    <div>{todoList?.title}</div>
  )
}
test.tsx
import type { ReactNode } from 'react'
 
export const Test = ({ children }: { children: ReactNode }) => {
  <>
    <button onClick={() => console.log('Test Client')}>Log</button>
    {children}
  </>
}
test.tsx
import { Test } from '@/components/test/test'
import { TestServer } from '@/components/test/test-server'
import Link from 'next/link'
import styles from './header.module.css'
 
export const Header = () => (
  <nav className={styles.navigation}>
    <ul>
      <li><Link href={'/'} className={styles.link}>Main</Link></li>
      <li><Link href={'/profile'} className={styles.link}>Profile</Link></li>
      <li><Link href={'/books'} className={styles.link}>Books</Link></li>
    </ul>
    <Test>
      <TestServer />
    </Test>
  </nav>
)

3.2 🌟 Стратегическая ценность паттерна

Этот подход реализует принцип разделения ответственности:

  • 🧩 Клиентский компонент управляет состоянием и интерактивностью
  • 🏗️ Серверный компонент отвечает за данные и рендеринг

🔧 Почему это работает

  • Серверный компонент полностью отрабатывает на сервере
  • В клиентский компонент передаётся уже готовый HTML
  • Код серверного компонента никогда не попадает в браузер

🚀 Преимущества подхода

  • 📦 Нулевое влияние на размер клиентского бандла
  • 🔐 Вся чувствительная логика остаётся на сервере
  • ⚡ Высокая производительность и масштабируемость

Освоение этого паттерна позволяет создавать сложные и интерактивные интерфейсы без компромиссов по производительности.

4️⃣ Заключение: ключевые архитектурные правила

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

1️⃣ Спускайте клиентскую директиву как можно глубже

Применяйте её только к тем компонентам, которые непосредственно требуют интерактивности 🌿

2️⃣ Компонуйте, а не импортируйте

Передавайте серверные компоненты в клиентские через props (чаще всего children), избегая прямого импорта 🚫

🏆 Итог Освоение этих двух паттернов — ключевой навык для разработки:

  • быстрых ⚡
  • масштабируемых 📈
  • SEO-дружелюбных 🌍 приложений на Next.js.