Практический опыт работы с серверными компонентами React

автор vadim


React остается флагманом среди интерфейсных фреймворков JavaScript, и команда React продолжает искать пути поддержания его актуальности. Одним из наиболее важных разработок в плане развития является React Server Components.

Серверные компоненты React предоставляют средства переноса работы над компонентом на сервер. Это позволяет избежать необходимости отправлять встроенный JavaScript и обслуживать вторичные запросы API для гидратации компонента.

Серверные компоненты React — это функция предварительной версии, которую можно включить в React 18.

Зачем использовать серверные компоненты React

Прежде чем мы рассмотрим, как будут работать серверные компоненты React, давайте подумаем, почему. Во-первых, полезно отметить, что серверные компоненты React отличаются от рендеринга на стороне сервера (SSR). Как говорится в RFC от команды React:

[SSR and Server Components are] дополнительный. SSR — это прежде всего метод быстрого отображения неинтерактивной версии клиент компоненты. Вам все равно придется оплатить стоимость загрузки, анализа и выполнения этих клиентских компонентов после загрузки исходного HTML.

Таким образом, в отличие от SSR, где мы стремимся визуализировать первоначальную версию компонента, который затем будет вести себя как обычное животное на стороне клиента, серверные компоненты React намерены полностью заменить функциональность на стороне клиента работой, выполняемой на сервере. Это имеет два основных преимущества:

  1. Связанный JavaScript не нужно пересылать клиенту по сети. JavaScript импортируется и выполняется, а результаты используются на сервере.
  2. Первоначальные запросы Ajax/API не требуются для гидратации компонента. Компонент может напрямую взаимодействовать с серверными службами для удовлетворения этих потребностей. Это делает клиента менее болтливым и позволяет избежать «водопада запросов», который иногда наблюдается, когда браузер выполняет взаимосвязанную выборку данных.

Ограничения серверных компонентов React

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

Основные ограничения, установленные спецификацией, по сравнению с обычными клиентскими компонентами:

  • Никакого использования состояния (например, useState() не поддерживается). Почему? Потому что компонент запускается один раз, а результаты передаются клиенту; т. е. компонент не работает на клиенте, поддерживающем состояние.
  • Никаких событий жизненного цикла, таких как useEffect(). Опять же, потому что компонент не выполняется в браузере, где он мог бы воспользоваться преимуществами событий и побочных эффектов.
  • Никаких API-интерфейсов только для браузера, таких как DOM, если только вы не заполните их на сервере. Подумайте, в частности, об API-интерфейсе выборки, где серверные механизмы рендеринга обычно предоставляют полифил, поэтому функциональность серверной стороны выглядит так же, как браузер в отношении вызовов API.
  • Никаких пользовательских перехватчиков, зависящих от состояния или эффектов, или служебных функций, зависящих от API-интерфейсов только браузера. Это всего лишь выпадения из исходящих ограничений.

Возможности, поддерживаемые серверными компонентами React, которые не поддерживаются клиентскими компонентами:

  • Использование только серверных источников данных, таких как базы данных, внутренние службы и файловые системы. Короче говоря, компонент имеет полный доступ к среде узла, в которой он находится.
  • Использование серверных хуков. Доступ к возможностям на стороне сервера, таким как файловая система, может быть заключен в перехватчики, чтобы использовать функциональность в том же духе, что и в обычных перехватчиках.
  • Возможность рендеринга других серверных компонентов, собственных элементов (div, span и т. д.), и клиентские компоненты.

Имейте это в виду. Серверные компоненты React существуют в иерархическом дереве компонентов, в котором сочетаются как серверные, так и клиентские компоненты, вложенные друг в друга.

Также обратите внимание, что серверные компоненты React никоим образом не заменяют другие части экосистемы React. В частности, серверные компоненты React не заменяют обычные клиентские компоненты. Скорее, они улучшают клиентские компоненты, позволяя вам вставлять в дерево, где это необходимо, компоненты, предназначенные только для сервера.

Кроме того, серверные компоненты React по-прежнему могут передавать реквизиты своим дочерним клиентским компонентам. Это означает, что вы можете разумно разделить свое приложение на интерактивные разделы, обрабатываемые клиентскими компонентами и содержащие серверные компоненты, которые заранее полностью загружают свое состояние из серверной части.

Использование компонентов сервера React

Поскольку сейчас существует два типа компонентов, вы различаете их, используя server.js и client.js (и другие связанные расширения, такие как server.jsx и client.jsx) для серверных и клиентских компонентов соответственно. Обратите внимание, что компоненты client.js не являются чем-то новым. Они точно такие же, как компоненты React, с которыми вы уже были знакомы, только теперь у них есть расширение файла, поэтому движок знает, что есть что.

Если вы посмотрите на демонстрационное приложение, созданное командой React, вы увидите, что файлы в каталоге /src перемешаны и зависят друг от друга. Например, есть файлы NoteList.server.js и SideBarNote.client.js.

Взгляните на исходный код NoteList.server.js в листинге 1.

Листинг 1. NoteList.server.js

import {fetch} from 'react-fetch';

import {db} from './db.server';
import SidebarNote from './SidebarNote';

export default function NoteList({searchText}) {

  // const notes = fetch('http://localhost:4000/notes').json();
  // WARNING: This is for demo purposes only.
  // We don't encourage this in real apps. There are far safer ways to access data in a real application!
  const notes = db.query(
    `select * from notes where title i like $1 order by id desc`,
    ['%' + searchText + '%']
  ).rows;

  // Now let's see how the Suspense boundary above lets us not block on this.
  // fetch('http://localhost:4000/sleep/3000');

  return notes.length > 0 ? (
    <ul className="notes-list">
      {notes.map((note) => (
        <li key={note.id}>
          <SidebarNote note={note} />
        </li>
      ))}
    </ul>
  ) : (
    <div className="notes-empty">
      {searchText
        ? `Couldn't find any notes titled "${searchText}".`
        : 'No notes created yet!'}{' '}
    </div>
  );
}

Здесь проиллюстрировано несколько вещей. Во-первых, обратите внимание на полифилл для API выборки в строке 1, предоставленный react-fetch. Опять же, это позволит вам писать запросы API, которые выглядят так же, как клиентские компоненты.

Во-вторых, обратите внимание на то, как доступ к хранилищу данных осуществляется через унифицированный API (интерфейс import из db). Это соглашение, предложенное командой React; теоретически вы можете получить доступ к базе данных через типичный API узла. В любом случае, notes переменная заполняется путем прямого обращения к базе данных, где код комментируется предупреждениями не делать этого в реальной жизни (она уязвима для SQL-инъекций).

В-третьих, обратите внимание, что тело шаблона представления представляет собой типичный JSX, определенный возвратом функции.

В-четвертых, и наконец, посмотрите, как SideBarNote Компонент импортируется так же, как и любой другой компонент, даже если это клиентский компонент, определенный в файле SideBarNote.client.js.

Компоненты без комплекта

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

Например, если вы посмотрите на Note.server.js, вы увидите, что он импортирует утилиту форматирования данных (через import {format} from 'date-fns';). Вместо архивирования и отправки, а затем разархивирования и выполнения все происходит на стороне сервера. Вы также избегаете уродливой альтернативы созданию собственного форматтера данных (фу).

Улучшенное разделение кода.

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

Это похоже на использование React.lazy() для импорта кода, за исключением того, что разделение происходит без вмешательства. Кроме того, серверный компонент может начать загрузку необходимого пути кода раньше, чем клиентский компонент, который должен ждать, пока путь принятия решения будет загружен и выполнен.

Пример, приведенный в RFC, приведен в листинге 2, который стоит кратко рассмотреть.

Листинг 2. Отложенная загрузка серверного компонента

import React from 'react';

// one of these will start loading *once rendered and streamed to the client*:
import OldPhotoRenderer from './OldPhotoRenderer.client.js';
import NewPhotoRenderer from './NewPhotoRenderer.client.js';

function Photo(props) {
  // Switch on feature flags, logged in/out, type of content, etc:
  if (FeatureFlags.useNewPhotoRenderer) {
    return <NewPhotoRenderer {...props} />;
  } else {
    return <OldPhotoRenderer {...props} />;
  }
}

В листинге 2 мы принимаем решение о том, какой компонент загружать (OldPhotoRenderer или NewPhotoRenderer) на основе флага (FeatureFlags.useNewPhotoRenderer). Если бы это было сделано с React.lazy, компонент здесь придется оценить в браузере, прежде чем необходимый вариант будет лениво загружен. Вместо этого при использовании серверного компонента использование Lazy не требуется, и как только этот код выполнится на сервере, правильный путь кода начнет лениво загружаться.

Как работают серверные компоненты React

Рассмотрим следующую цитату из RFC React Server Components:

Серверные компоненты визуализируются постепенно и постепенно передают отображаемые единицы пользовательского интерфейса клиенту. В сочетании с приостановкой это позволяет разработчикам создавать намеренные состояния загрузки и быстро показывать важный контент, ожидая загрузки оставшейся части страницы.

Интересный. Таким образом, серверные компоненты React на самом деле не делают что-то вроде SSR, при котором компонент отображается на сервере и сводится к HTML и минимуму JS для загрузки на клиенте. Вместо этого платформа фактически передает очищенное состояние пользовательского интерфейса, как только оно будет готово.

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

Это означает, как указано в цитате RFC, что основные элементы пользовательского интерфейса могут быть идентифицированы и отображены как можно скорее. Между тем, Suspense можно использовать для интеллектуальной обработки интерактивных частей на стороне клиента.

React встречает сервер

Серверные компоненты React представляют собой смелый шаг для такого популярного проекта JavaScript, поддерживаемого корпорациями. Он ясно заявляет всему миру, что React и его команда стремятся участвовать в продолжающейся суете инноваций среди фреймворков JavaScript.

Не желая почивать на лаврах, команда React работает над теми же вопросами, над которыми думают другие новаторы от Svelte до Qwik и Solid (а также Марко и Astro).

Узнайте больше о разработке JavaScript:

  • Практический опыт работы с GatsbyJS
  • Простой автоматизированный конвейер сборки для Node.js
  • Практический опыт работы с REST API Dropwizard
  • Практический опыт работы с MarkoJS
  • Практический опыт работы с SolidJS
  • Практический опыт работы со SvelteKit
  • Практический опыт со Svelte
  • Практический опыт работы с MarkoJS
  • Как использовать Auth0 с Node.js и Express
  • Как использовать FilePond с Node.js
  • Практический опыт работы с CodeSandBox
  • 7 инструментов, меняющих разработку JavaScript
  • 10 советов по настройке производительности React UI
  • Как использовать параллельный режим React
  • Как обрабатывать взаимодействие компонентов в React
  • Как обрабатывать ошибки в React
  • Как использовать функциональные компоненты React

Related Posts

Оставить комментарий