Понимание React Server Components
Серверные компоненты React (RSC) дополняют основы React, помимо того, что они являются чистой библиотекой рендеринга, и включают в себя получение данных и удаленную связь клиент-сервер в рамках инфраструктуры.
Ниже мы расскажем, почему необходимо создавать RSC, что они делают лучше всего и когда их использовать. Мы также коснемся того, как Next.js упрощает и улучшает детали реализации RSC через App Router.
Зачем нам нужны серверные компоненты?
Взгляните на мир до появления React. С такими языками, как PHP, у нас была более тесная связь между клиентом и сервером. В монолитной архитектуре вы можете обратиться к серверу для вызова данных прямо на создаваемой вами странице. Однако были и недостатки — а именно, сложность масштабирования монолитных приложений из-за межкомандных зависимостей и высоких требований к трафику.
React был создан для возможности компоновки и постепенного внедрения в существующие базы кода. Отвечая на мир, жаждущий богатой интерактивности, он разделил заботы клиента и сервера, сделав интерфейс намного более гибким в создании. Это было особенно важно для команд: два компонента React, каждый из которых создан отдельными разработчиками, могли работать вместе , поскольку работали в одной и той же среде.
Чтобы добиться этого, React пришлось внедрить инновации поверх уже существовавших веб-стандартов. За последнее десятилетие эволюции между многостраничными приложениями (MPA) и одностраничными приложениями (SPA), между рендерингом на стороне клиента и на стороне сервера цель осталась прежней : быстро предоставлять данные, обеспечивать богатую интерактивность и поддерживать отличный опыт разработчика.
Что разрешали серверный рендеринг и React Suspense?
На пути к тому, где мы находимся сейчас — серверным компонентам — были и другие проблемы, которые необходимо было решить. Чтобы лучше понять необходимость RSC, полезно сначала осознать необходимость рендеринга на стороне сервера (SSR) и приостановки.
SSR фокусируется на начальной загрузке страницы, отправляя предварительно обработанный HTML-код клиенту, который затем необходимо гидратировать с помощью загруженного JavaScript, прежде чем он начнет вести себя как типичное приложение React. SSR также происходит только один раз: при непосредственном переходе на страницу.
Используя только SSR, пользователь получает HTML быстрее, но ему приходится ждать водопада «все или ничего», прежде чем он сможет взаимодействовать с JavaScript:
- Все данные должны быть получены с сервера, прежде чем их можно будет отобразить.
- Весь JavaScript должен быть загружен с сервера, прежде чем клиент сможет быть с его помощью гидратирован.
- Вся гидратация должна быть завершена на клиенте, прежде чем с чем-либо можно будет взаимодействовать.
Чтобы решить эту проблему, React создал Suspense , который позволяет осуществлять потоковую передачу HTML на стороне сервера и выборочную гидратацию на клиенте. Обернув компонент в <Suspense>
, вы можете указать серверу снизить приоритет рендеринга и гидратации этого компонента, позволяя другим компонентам загружаться, не блокируясь более тяжелыми.
Если у вас есть несколько компонентов в <Suspense>
, React работает по дереву в том порядке, в котором вы написали, что позволяет оптимально осуществлять потоковую передачу вашего приложения. Однако если пользователь попытается взаимодействовать с определенным компонентом, этот компонент будет иметь приоритет над другими.
Это значительно улучшает ситуацию, но все же оставляет несколько оставшихся проблем:
- Данные для всей страницы должны быть получены с сервера, прежде чем можно будет отобразить какие-либо компоненты. Единственный способ обойти эту проблему — получить данные на стороне клиента с помощью перехватчика
useEffect()
, который имеет более длительный путь, чем выборка на стороне сервера, и происходит только после того, как компонент визуализируется и гидратируется. - Весь код JavaScript страницы в конечном итоге загружается, даже если он передается в браузер асинхронно. По мере увеличения сложности приложения увеличивается и объем кода, загружаемого пользователем.
- Несмотря на оптимизацию гидратации, пользователи по-прежнему не могут взаимодействовать с компонентами, пока клиентский JavaScript не будет загружен и реализован для этого компонента.
- Большая часть вычислительной мощности JavaScript по-прежнему приходится на клиент, который может работать на любом устройстве. Почему бы не перенести его на более мощный и предсказуемый сервер?
В Next.js без серверных компонентов React для извлечения данных требуется дополнительный уровень API.
Поскольку веб-стандарты догоняют возможности JavaScript-фреймворков, пришло время сделать еще один шаг вперед. Есть лучший способ создавать более быстрые приложения.
Что делают серверные компоненты React?
Чтобы решить вышеуказанные проблемы, React создал серверные компоненты. RSC индивидуально извлекают данные и полностью визуализируют их на сервере, а полученный HTML-код передается в дерево компонентов React на стороне клиента, чередуясь с другими серверными и клиентскими компонентами по мере необходимости.
Этот процесс устраняет необходимость повторного рендеринга на стороне клиента, тем самым повышая производительность. Для любых клиентских компонентов гидратация может происходить одновременно с потоковой передачей RSC, поскольку вычислительная нагрузка распределяется между клиентом и сервером.
Другими словами, сервер, гораздо более мощный и физически ближе к вашим источникам данных, занимается рендерингом с интенсивными вычислениями и отправляет клиенту только интерактивные фрагменты кода.
Когда RSC необходимо повторно отрисовать из-за изменения состояния, он обновляется на сервере и легко объединяется с существующим DOM без жесткого обновления . В результате состояние клиента сохраняется даже при обновлении частей представления с сервера.
RSC: производительность и размер пакета
RSC могут помочь уменьшить размер клиентского пакета JavaScript и повысить производительность загрузки.
Традиционно во время навигации по приложению клиент загружает, а затем выполняет все зависимости кода и данных. Без платформы React, поддерживающей разделение кода , это также означает отправку пользователям постороннего кода, не необходимого для страницы, на которой они находятся.
Однако RSC разрешают все зависимости на сервере, ближе к источникам данных вашего приложения. Они также отображают код только на сервере, который справляется с этой задачей намного быстрее, чем клиентские машины (например, мобильные телефоны). Затем React отправляет в браузер только эти обработанные результаты плюс клиентские компоненты.
Другими словами, с помощью серверных компонентов начальная загрузка страницы происходит быстрее и экономнее. Базовая среда выполнения на стороне клиента кэшируется, имеет предсказуемый размер и не увеличивается по мере роста вашего приложения. Дополнительный JavaScript, ориентированный на пользователя, добавляется в первую очередь, поскольку вашему приложению требуется больше интерактивности на стороне клиента через клиентские компоненты.
RSC: интеграция чередования и приостановки
RSC полностью чередуются с клиентским кодом, а это означает, что клиентские и серверные компоненты могут отображаться в одном дереве React. Перемещая большую часть кода вашего приложения на сервер, RSC помогают предотвратить водопады при получении данных на стороне клиента, быстро разрешая зависимости данных на стороне сервера.
При традиционном рендеринге на стороне клиента компоненты используют React Suspense, чтобы «приостановить» процесс рендеринга (и отобразить резервное состояние) в ожидании завершения асинхронной работы. При использовании RSC и выборка, и рендеринг данных происходят на сервере, поэтому Suspense также управляет периодом ожидания на стороне сервера, сокращая общий цикл обработки и ускоряя рендеринг резервной и завершенной страницы.
Важно отметить, что клиентские компоненты по-прежнему подвергаются SSR при начальной загрузке . Модель RSC не заменяет SSR или Suspense, а работает параллельно с ними, предоставляя пользователям все части вашего приложения по мере необходимости.
В Next.js с серверными компонентами React извлечение данных и рендеринг пользовательского интерфейса могут выполняться из одного и того же компонента. Кроме того, действия сервера предоставляют пользователям возможность взаимодействовать с данными на стороне сервера до загрузки JavaScript на странице.
RSC: ограничения
Весь код, написанный для серверных компонентов, должен быть сериализуемым, что означает, что вы не можете использовать перехватчики жизненного цикла, такие как useEffect()
или состояние.
Однако вы по-прежнему можете взаимодействовать с сервером из клиента через действия сервера, о которых мы поговорим чуть позже.
Кроме того, RSC не поддерживают непрерывные обновления, например, через WebSockets. В этих случаях потребуется метод выборки или опроса на стороне клиента.
Как использовать серверные компоненты React
Прелесть RSC в том, что вам не нужно полностью знать, как они работают, чтобы воспользоваться их преимуществами. В App Router, представленном в Next.js 13.4, который предлагает наиболее полнофункциональную реализацию RSC, все компоненты по умолчанию являются серверными компонентами.
Если вы хотите использовать события жизненного цикла, такие как useEffect()
или состояние, вам необходимо встроить клиентский компонент. Чтобы выбрать клиентский компонент, необходимо написать «использовать клиент» в верхней части компонента, но для получения более сложных советов мы рекомендуем вам просмотреть документацию .
Балансировка серверных и клиентских компонентов
Важно отметить, что RSC не предназначены для замены клиентских компонентов. Работоспособное приложение использует как RSC для динамической выборки данных, так и клиентские компоненты для обеспечения богатой интерактивности. Задача заключается в том, чтобы определить, когда использовать каждый компонент.
Как разработчик, рассмотрите возможность использования RSC для рендеринга на стороне сервера и получения данных, полагаясь при этом на клиентские компоненты для локальных интерактивных функций и взаимодействия с пользователем. Найдя правильный баланс, вы сможете создать высокопроизводительное, эффективное и привлекательное приложение.
Самое главное, чтобы вы продолжали тестировать свои приложения в нестандартных средах: эмулировали более медленные компьютеры, более медленные телефоны и более медленный Wi-Fi, и вы можете быть удивлены, увидев, насколько лучше ваше приложение работает при правильном сочетании компонентов.
RSC не являются полным решением проблемы обременения пользователей слишком большим количеством клиентского JavaScript, но они, безусловно, дают нам возможность выбирать, когда переносить нагрузку на пользовательское устройство.
Улучшена выборка данных с помощью Next.js.
RSC извлекают данные на сервере, что не только обеспечивает безопасный доступ к внутренним данным, но и повышает производительность за счет сокращения взаимодействия между сервером и клиентом. В сочетании с улучшениями Next.js RSC также обеспечивают интеллектуальное кэширование данных, множественную выборку за один цикл и автоматическую fetch()
дедупликацию запросов — все это максимизирует эффективность отправки данных на стороне клиента.
Возможно, самое важное то, что выборка данных на сервере помогает предотвратить водопады выборки данных на стороне клиента, когда запросы накапливаются друг против друга и должны обрабатываться последовательно, прежде чем пользователь сможет продолжить работу. При извлечении на стороне сервера накладные расходы гораздо меньшие, поскольку они не блокируют весь клиент и разрешаются гораздо быстрее.
Кроме того, вам больше не нужны специфичные для Next.js методы уровня страницы, такие как getServerSideProps()
и getStaticProps()
, которые не обеспечивали достаточно детального контроля для отдельных компонентов и имели тенденцию к избыточной выборке данных. (Когда пользователь переходил на страницу, все данные были получены независимо от того, с какими компонентами он фактически взаимодействовал.)
В маршрутизаторе приложений Next.js все извлеченные данные теперь по умолчанию являются статическими и обрабатываются во время сборки. Однако это можно легко изменить: Next.js расширяет fetch
объект параметров, обеспечивая гибкость кэширования и повторной проверки правил.
Вы можете использовать эту {next: {revalidate: number}}
опцию для обновления статических данных через заданные интервалы времени или при возникновении внутренних изменений (инкрементная статическая регенерация), а эту {cache: 'no-store'}
опцию можно передать в запросе на выборку динамических данных (рендеринг на стороне сервера).
Все это делает серверные компоненты React в маршрутизаторе приложений Next.js мощным инструментом для эффективной, безопасной и динамической выборки данных, которые по умолчанию кэшируются для обеспечения высокопроизводительного взаимодействия с пользователем.
Действия сервера: первые шаги React к изменчивости
В контексте RSC действия сервера — это функции, которые вы определяете в RSC на стороне сервера, которые затем можно передать через границу сервер/клиент. Когда пользователь взаимодействует с вашим приложением на стороне клиента, он может напрямую вызывать действия сервера, которые будут безопасно выполняться на стороне сервера.
Этот подход обеспечивает беспрепятственный удаленный вызов процедур (RPC) между клиентом и сервером. Вместо написания отдельного маршрута API для связи с сервером вы можете напрямую вызывать действия сервера из клиентских компонентов.
Также помните, что маршрутизатор приложений Next.js полностью построен на интеллектуальном кэшировании, повторной проверке и изменении данных. Действия сервера в Next.js означают, что вы можете как изменять кеш, так и обновлять дерево React в одном и том же запросе на сервер — и все это при сохранении целостности кэша клиента посредством навигации.
В частности, действия сервера предназначены для выполнения таких задач, как обновление базы данных или отправка форм. Например, они могут постепенно улучшать ваши формы, а это означает, что даже если JavaScript еще не загружен, пользователь все равно может взаимодействовать с формой, а действия сервера будут обрабатывать отправку и обработку данных формы.
Возможности, которые предлагают действия сервера, как для постепенного улучшения, так и для устранения работы по разработке API, отлично подходят для доступности, удобства использования и опыта разработчиков.
Пусть Next.js сделает всю тяжелую работу
Next.js — это первая платформа, интегрирующая всю архитектуру React для серверных компонентов, действий сервера, приостановки, переходов и всего остального, что изменилось с выпуском RSC.
Пока вы сосредотачиваетесь на создании своего продукта, Next.js использует стратегическую потоковую передачу и интеллектуальное кэширование, чтобы гарантировать, что рендеринг вашего приложения не блокируется и передает динамические данные на максимальной скорости.
Next.js стремится оставаться на переднем крае новых функций React, не жертвуя при этом стабильностью, надежностью и обратной совместимостью. Он по-прежнему будет предлагать умные настройки по умолчанию для вашей команды, позволяющие быстро выполнять итерации, сохраняя при этом гибкость и масштабируемость для проектов любого масштаба.
Итог
Подведем итог: серверные компоненты React предлагают собственный способ взаимодействия с сервером прямо внутри компонента, облегчая как код, так и когнитивную нагрузку при взаимодействии с динамическими данными. Клиентские компоненты остаются полностью функциональными и полностью пригодными к использованию, как и раньше. Ваша новая задача — выбирать, когда использовать каждый из них.