[JavaScript, ReactJS] React Server Components — что это?

Автор Сообщение
news_bot ®

Стаж: 6 лет 9 месяцев
Сообщений: 27286

Создавать темы news_bot ® написал(а)
28-Дек-2020 04:32

Буквально неделю назад, команда реакт опубликовала в своем блоге о новом RFC. Давайте разберемся, что это за зверь и зачем он нужен.
Что этоКак понятно из названия React Server Components - это компоненты которые исполняются на сервере. Теперь у нас есть несколько видов компонентов:
  • Клиентские компоненты
  • Серверные компоненты
  • Гибридные компоненты
Клиентские компонентыЭто те компоненты которые мы пишем сейчас, используя состояние и эффекты, они позволяют взаимодействовать с пользователем и исполняются в браузере. До этого момента у нас существовали только клиентские компоненты. Теперь клиентские компоненты имеют постфикс в названии .client.jsСерверные компонентыЭто те компоненты которые исполняются на сервере, они не могут использовать состояние и эффекты(в них запрещено использование useState и любых других "клиентских" хуков). Они могут быть перезапрошены по ходу исполнения приложения. Имеют доступ ко всей инфраструктуре сервера. Такие компоненты имеют постфикс в названии .server.js. Также в качестве параметров не может передавать функции, только данные.Гибридные компонентыЭти компоненты могут исполняться как на сервере так и на клиенте. У них самые сильные ограничения. Они не могут использовать клиентские хуки и серверную инфраструктуру. Фактически могут только содержать JSX разметку.Различие с SSRДочитав до этого момента многие из вас спросят себя, а в чем различие с SSR и инструментами вроде Next.js. Ведь реакт и раньше умел исполняться на сервере, верно? Не совсем так. Различие заключается в том, что SSR возвращает нам HTML разметку, которую после этого необходимо гидрировать. Чаще всего SSR используется для первой загрузки приложения, чтобы пользователь не видел белый экран. При этом Server Components возвращает JSON структуру части virtual dom.
Картинка слева, это пример того, что передается по сети при использовании server components.
На данный момент ничего не сказано о совмещении SSR и server components, но я думаю в будущем эти два подхода можно будет совмещать.
Пример использования
Картинка выше демонстрирует, то как выглядит дерево компонентов. Раньше оно всегда состояло из клиентских компонент, теперь дерево может содержать серверные компоненты.
При готовом окружении, чтобы начать использовать серверные компоненты, необходимо создать файл с постфиксом .server.js
// Note.server.js - Server Component
import db from 'db.server';
// (A1) We import from NoteEditor.client.js - a Client Component.
import NoteEditor from 'NoteEditor.client';
function Note(props) {
  const {id, isEditing} = props;
  // (B) Can directly access server data sources during render, e.g. databases
  const note = db.posts.get(id);
  return (
    <div>
      <h1>{note.title}</h1>
      <section>{note.body}</section>
      {/* (A2) Dynamically render the editor only if necessary */}
      {isEditing
        ? <NoteEditor note={note} />
        : null
      }
    </div>
  );
}
Вот мы и создали первый серверный компонент.Зачем нужноКогда добавляют новые фичи это прикольно, но у них должна быть практическая польза, вот что описывает команда реакт:Zero-Bundle-Size ComponentsВсе мы любим зависимости в нашем приложении, но иногда их становится так много, что счет объема у приложения идет на десятки мегабайт. Так как мы получаем доступ к выполнению на сервере, то теперь и наши зависимости могут выполнять свои функции на сервере, без передачи исходного кода на клиент. Представьте, что вы пишите приложения для отображения markdown текста, сейчас вы напишите так:
// NoteWithMarkdown.js
// NOTE: *before* Server Components
import marked from 'marked'; // 35.9K (11.2K gzipped)
import sanitizeHtml from 'sanitize-html'; // 206K (63.3K gzipped)
function NoteWithMarkdown({text}) {
  const html = sanitizeHtml(marked(text));
  return (/* render */);
}
При это к размеру bundle добавиться 74кб дополнительно, но если этот компонент мы превратим с server components, то получим следующее:
// NoteWithMarkdown.server.js - Server Component === zero bundle size
import marked from 'marked'; // zero bundle size
import sanitizeHtml from 'sanitize-html'; // zero bundle size
function NoteWithMarkdown({text}) {
  const html = sanitizeHtml(marked(text));
  return (/* render */);
}
В данном примере изменилось только название файла, но при этом мы экономим 74кб исполняя данную часть кода на сервере.Полный доступ к BackendТак как компоненты исполняются на сервере, можно получать полный доступ к окружению сервера:
// Note.server.js - Server Component
import fs from 'react-fs';
function Note({id}) {
  const note = JSON.parse(fs.readFile(`${id}.json`));
  return <NoteWithMarkdown note={note} />;
}
// Note.server.js - Server Component
import db from 'db.server';
function Note({id}) {
  const note = db.notes.get(id);
  return <NoteWithMarkdown note={note} />;
}
Только для доступа к окружения сервера, требуются обертки для реакта, на данный момент команда реакта разработала 3 обертки:
  • react-fs - Обертка для работы с файлами
  • react-fetch - Обертка для работы с сетью
  • react-pg - Обертка для работы с PostgresSql
Думаю в ближайшем будущем, у нас появится API с помощью которого можно будет создавать собственный обертки для работы с server component.Автоматический Code SplittingЕсли вы работаете с реакт, то вы уже знакомы с понятием Code Splitting. Это процесс когда компоненты загружаются по необходимости, для того, чтобы уменьшить размер нашего bundle мы используем динамические модули и React.lazy:
// PhotoRenderer.js
// NOTE: *before* Server Components
import React from 'react';
// one of these will start loading *when rendered on the client*:
const OldPhotoRenderer = React.lazy(() => import('./OldPhotoRenderer.js'));
const NewPhotoRenderer = React.lazy(() => import('./NewPhotoRenderer.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} />;
  }
}
При использовании Server Components, мы получаем данную возможность по умолчанию:
// PhotoRenderer.server.js - Server Component
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} />;
  }
}
Так как данный компонент исполняется на сервере, то на клиент будет отправлен уже необходимый компонент, что позволяет писать более привычный и интуитивный код.Отсутствие client-server водопадовВодопад - это когда нам после рендера необходимо запросить данные с сервера, после их получения, возможно нам требуется получить еще какие то данные и таким образом нам необходимо выполняться множество запросов на сервер, а пользователю ожидать выполнения этих запросов.
Такой подход ухудшает пользовательский опыт при использовании приложения. Существуют разные решения, чаще всего они касаются API. Например graphql/JSON API и другие.Чаще всего мы пишем компоненты следующим образом:
// Note.js
// NOTE: *before* Server Components
function Note(props) {
  const [note, setNote] = useState(null);
  useEffect(() => {
    // NOTE: loads *after* rendering, triggering waterfalls in children
    fetchNote(props.id).then(noteData => {
      setNote(noteData);
    });
  }, [props.id]);
  if (note == null) {
    return "Loading";
  } else {
    return (/* render note here... */);
  }
}
Тут мы загрузили сам компонент Note, после этого выполнили рендер и только после этого запросили данные с сервера. Теперь с server components мы можем это делать не последовательно, а одновременно:
// Note.server.js - Server Component
function Note(props) {
  // NOTE: loads *during* render, w low-latency data access on the server
  const note = db.notes.get(props.id);
  if (note == null) {
    // handle missing note
  }
  return (/* render note here... */);
}
Таким образом, когда компонент исполняется на сервере, он может выполнить взаимодействие с необходимым апи и передать данные в компонент за одну итерацию по сети для пользователя, после чего результат выполнения компонента стримится пользователю
ИтогНа мой взгляд server components имеет место быть, сочетая Suspence, Concurent Mode и Server Components можно гибко для разработчиков и удобно для пользователя реализовывать UI.Не забывайте это RFC и подход, реализации и API может поменяться до официального релиза.Что думаете по поводу Server Components?Дополнительный материалЕсли хотите более детально погрузиться в тему Server Components.Выступление Дэна АбрамоваДокументация RFCПример приложения
===========
Источник:
habr.com
===========

Похожие новости: Теги для поиска: #_javascript, #_reactjs, #_frontend, #_react, #_javascript, #_redux, #_html, #_javascript, #_reactjs
Профиль  ЛС 
Показать сообщения:     

Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы

Текущее время: 22-Ноя 23:25
Часовой пояс: UTC + 5