Глава 9 Серверные компоненты React

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

Книга
React. К вершинам мастерства

Fluent React: Build Fast, Performant, and Intuitive Web Applications

Глубокое погружение во внутреннее устройство React: JSX и продвинутые паттерны, виртуальный DOM и реконциляция, серверный рендеринг, конкурентный режим и серверные компоненты.

Программа вечера

Антон Помазков
Антон Помазков
Артём Никифоров

Предыдущая глава: Зачем нужен фреймворк?

Абстрагирование

Фреймворк скрывает сложные детали: маршрутизацию, гидратацию, сборку, кэширование. Разработчик сосредотачивается на бизнес-логике.

Соглашения

Единые правила структуры файлов, именования и потоков данных ускоряют командную работу и снижают когнитивную нагрузку.

Комплексные решения

Фреймворк даёт готовый набор инструментов: рендеринг, маршрутизацию, оптимизацию ассетов, работу с данными.

Производительность

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

сегодня

Серверные компоненты React (RSC)

В прошлой главе их лишь немного упомянули — сегодня разберём подробнее: что это за компоненты, где они выполняются и что попадает в клиентский бандл.

Нажмите на блок — он раскроется, остальные свернутся до иконок

Особенности RSC

1

«Запускаются» на сервере

Функция компонента вызывается и выполняется в серверной среде — во время сборки или при запросе. В браузере она не выполняется никогда.

2

Исключаются из клиентского пакета JavaScript

JS-код RSC не попадает в клиентский бандл — пользователь скачивает только результат его работы.

Преимущество RSC над клиентскими

Серверные компоненты могут стартовать во время сборки (build time) Build time — этап сборки приложения: код выполняется один раз, ещё до того как страницу запросил первый пользователь. Результат «вшивается» в готовую разметку, поэтому на запрос ничего пересчитывать не нужно. Противоположность — request time, когда компонент выполняется на каждый запрос. — и тогда им доступны операции, которых в браузере просто нет.

01

Чтение из файловой системы

Импорт Markdown-файлов, локальных JSON, конфигураций. В браузере прямой доступ к файловой системе невозможен или сильно ограничен — на сервере при сборке это естественная операция.

import fs from "node:fs/promises";

const Post = async () => {
  const md = await fs.readFile(
    "posts/hello.md", "utf8"
  );
  return <article>{md}</article>;
};
02

Извлечение статического содержимого

Данные из CMS, статических API, внешних источников — на этапе сборки. Контент «фиксируется» заранее, и его не приходится запрашивать при каждом обращении пользователя.

const Posts = async () => {
  const posts = await fetch(
    "https://cms.example/api/posts"
  ).then((r) => r.json());

  return <List items={posts} />;
};
03

Доступ к уровню данных

Серверные компоненты обращаются напрямую к базам данных, внутренним сервисам и ORM-слою. Для простых операций чтения отдельный API-слой становится не нужен.

import { db } from "./db";

const Users = async () => {
  const users = await db
    .select().from(usersTable);

  return <Table rows={users} />;
};

Как работает RSC?

Этап 01 из 06

Создаём RSC

RSC — это особый тип компонента React Компонент React — это обычная JavaScript-функция, которая принимает пропсы и возвращает элемент React: описание того, каким должен быть UI. Не HTML-строку и не DOM-узел. JSX внутри компилируется в вызовы React.createElement. , который выполняется только на сервере.

// минимальный серверный компонент
const Greeting = async () => {
  return <div>Hi!</div>;
};
Этап 02 из 06

Вызываем RSC на сервере

Функция компонента вызывается и выполняется в серверной среде — во время сборки (build time) или при запросе (request time). В браузере она не выполняется никогда.

// на сервере, с await: компонент может быть асинхронным
const element = await Greeting();
Этап 03 из 06

RSC возвращает элемент React

Серверный компонент возвращает элемент React Элемент — это JSON-подобный объект, у которого есть свойства: $$typeof, type ('div', 'span' или ссылка на другой компонент), props вместе с children и вложенными элементами. — ровно то же, что и клиентский компонент.

// что вернул наш Greeting
{
  $$typeof: Symbol(react.element),
  type: "div",
  props: { children: "Hi!" }
}
Этап 04 из 06

Сериализуем RSC в payload

Дерево элементов — обычный объект, поэтому его можно превратить в поток для передачи. Мешает только $$typeof: его значение — Symbol, а символы не сериализуются, поэтому маркер заменяют строкой.

// сериализованный элемент
'{"$$typeof":"react.element",
  "type":"div",
  "props":{"children":"Hi!"}}'
Этап 05 из 06

Отправляем payload по сети

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

// ответ сервера — поток, а не бандл
res.end(serializedTree);
Этап 06 из 06

Клиент рендерит результат RSC

React на клиенте восстанавливает дерево (символ-маркер возвращается на место) и рендерит его. Клиентские компоненты при этом остаются привычными — с хуками, состоянием и обработчиками событий.

// на клиенте
const element = JSON.parse(payload, reviver);
root.render(element);

Преимущества RSC

Преимущество 01

Предсказуемая производительность

Предсказуемость

  • код можно профилировать и оптимизировать;
  • можно гарантировать уровень сервиса (SLA);
  • нет деградации на слабых устройствах;
  • масштабирование планируется заранее.

Мощности, которые мы контролируем

  • сколько CPU и RAM;
  • какая версия Node.js или среды выполнения;
  • какие ограничения и квоты заданы;
  • как масштабируется нагрузка.

Не считаем на чужих устройствах

Парк устройств разнороден: от мощных десктопов до старых смартфонов с медленным CPU и малой памятью. Тяжёлый код на клиенте — это вычисления, которые мы не контролируем.

Преимущество 02

Безопасность серверных сред и защита секретов

01

Защищённые серверные среды

Дата-центры, приватные сети, переменные окружения, секреты в CI/CD — среда, доступ к которой контролируем мы, а не конечный пользователь.

02

Безопасные операции

  • обращение к базам данных с учётными данными;
  • вызовы внутренних API с секретными ключами;
  • проверка прав доступа;
  • работа с платёжными системами и токенами.
03

Секреты не утекают в браузер

На клиенте те же операции означали бы, что любой может:

  • посмотреть исходный код бандла;
  • перехватить сетевые запросы;
  • извлечь токены и ключи.

Важное ограничение

Преимущество работает, пока секрет не передан клиенту сами. Отдали секретное значение пропсом в клиентский компонент ('use client') — оно сериализуется и уедет в браузер. Правило: секреты живут только внутри серверных компонентов.

Преимущество 03

Асинхронность серверных компонентов

В клиентском React компонент не может быть async-функцией: React ждёт синхронный возврат JSX, а данные приходят через хуки (useEffect, Suspense, use()). У серверных компонентов этого ограничения нет — можно объявить async и напрямую await-ить загрузку.

Почему так можно

Сервер контролирует момент отправки ответа, поэтому он может:

  • начать выполнение асинхронного компонента;
  • дождаться разрешения всех промисов;
  • и только потом сериализовать результат и ответить.

В браузере это дороже

Там React не может «заблокировать» рендеринг в ожидании данных без сложных механизмов. На сервере ожидание — просто задержка ответа, которую мы полностью контролируем.

Что далее

Преимущества Пройдено
Антон Помазков
Антон Помазков
Артём Никифоров