Компоненты сервера
Компоненты сервера React
Компоненты сервера предназначены для использования в React Server Components.
Компоненты сервера — это новый тип компонентов, которые рендерятся заранее, до объединения, в среде, отличной от приложения клиента или сервера SSR.
Эта отдельная среда — это «сервер» в React Server Components. Компоненты сервера могут выполняться один раз во время сборки на сервере CI или они могут выполняться для каждого запроса с помощью веб-сервера.
- Компоненты сервера без сервера
- Компоненты сервера с сервером
- Добавление интерактивности в компоненты сервера
- Асинхронные компоненты с компонентами сервера
Примечание
Как создать поддержку компонентов сервера?
Хотя компоненты сервера React в React 19 стабильны и не будут вызывать ошибки между основными версиями, основанные на них API, используемые для реализации сборщика или фреймворка компонентов сервера React, не следуют принципу semver и могут вызывать ошибки между младшими версиями в React 19.x.
Чтобы поддерживать компоненты сервера React как сборщик или фреймворк, мы рекомендуем зафиксировать конкретную версию React или использовать релиз Canary. Мы продолжим сотрудничество со сборщиками и фреймворками, чтобы стабилизировать API, используемые для реализации компонентов сервера React в будущем.
Компоненты сервера без сервера
Компоненты сервера могут выполняться во время сборки для чтения из файловой системы или получения статического контента, поэтому веб-сервер не требуется. Например, вы можете читать статические данные из системы управления контентом.
Без компонентов сервера обычно статические данные извлекаются на клиенте с помощью эффекта:
// bundle.js
import marked from 'marked'; // 35.9K (11.2K gzipped)
import sanitizeHtml from 'sanitize-html'; // 206K (63.3K gzipped)
function Page({page}) {
const [content, setContent] = useState('');
// NOTE: loads *after* first page render.
useEffect(() => {
fetch(`/api/content/${page}`).then((data) => {
setContent(data.content);
});
}, [page]);
return <div>{sanitizeHtml(marked(content))}</div>;
} // api.js
app.get(`/api/content/:page`, async (req, res) => {
const page = req.params.page;
const content = await file.readFile(`${page}.md`);
res.send({content});
}); Этот шаблон означает, что пользователям необходимо загрузить и разобрать дополнительные 75К (сжатые) библиотек и подождать второго запроса для получения данных после загрузки страницы, только для того, чтобы отобразить статический контент, который не будет меняться в течение всего срока действия страницы.
С компонентами сервера вы можете отобразить эти компоненты один раз во время сборки:
import marked from 'marked'; // Not included in bundle
import sanitizeHtml from 'sanitize-html'; // Not included in bundle
async function Page({page}) {
// NOTE: loads *during* render, when the app is built.
const content = await file.readFile(`${page}.md`);
return <div>{sanitizeHtml(marked(content))}</div>;
} Полученный результат может быть отображён на сервере (SSR) в HTML и загружен на CDN. При загрузке приложения клиент не увидит исходный Page компонент или дорогостоящие библиотеки для отображения разметки Markdown. Клиент увидит только отрендеренный результат:
<div><!-- html for markdown --></div> Это означает, что контент виден во время первой загрузки страницы, и сборка не включает дорогостоящие библиотеки, необходимые для отображения статического контента.
Примечание
Вы можете заметить, что компонент сервера выше — это асинхронная функция:
async function Page({page}) {
//...
}
Асинхронные компоненты — это новая функция компонентов сервера, которая позволяет вам await в рендере.
Компоненты сервера с сервером
Компоненты сервера также могут выполняться на веб-сервере во время запроса страницы, позволяя вам получить доступ к вашему слою данных без необходимости создания API. Они отображаются до объединения вашего приложения и могут передавать данные и JSX в качестве свойств клиентским компонентам.
Без компонентов сервера обычно динамические данные извлекаются на клиенте в эффекте:
// bundle.js
function Note({id}) {
const [note, setNote] = useState('');
// NOTE: loads *after* first render.
useEffect(() => {
fetch(`/api/notes/${id}`).then(data => {
setNote(data.note);
});
}, [id]);
return (
<div>
<Author id={note.authorId} />
<p>{note}</p>
</div>
);
}
function Author({id}) {
const [author, setAuthor] = useState('');
// NOTE: loads *after* Note renders.
// Causing an expensive client-server waterfall.
useEffect(() => {
fetch(`/api/authors/${id}`).then(data => {
setAuthor(data.author);
});
}, [id]);
return <span>By: {author.name}</span>;
} // api
import db from './database';
app.get(`/api/notes/:id`, async (req, res) => {
const note = await db.notes.get(id);
res.send({note});
});
app.get(`/api/authors/:id`, async (req, res) => {
const author = await db.authors.get(id);
res.send({author});
}); С компонентами сервера вы можете прочитать данные и отобразить их в компоненте:
import db from './database';
async function Note({id}) {
// NOTE: loads *during* render.
const note = await db.notes.get(id);
return (
<div>
<Author id={note.authorId} />
<p>{note}</p>
</div>
);
}
async function Author({id}) {
// NOTE: loads *after* Note,
// but is fast if data is co-located.
const author = await db.authors.get(id);
return <span>By: {author.name}</span>;
} Затем сборщик объединяет данные, отрендеренные компоненты сервера и динамические клиентские компоненты в сборку. При необходимости эта сборка может быть отображена на сервере (SSR) для создания начального HTML-кода страницы. При загрузке страницы браузер не видит исходные Note и Author компоненты; только рендерный результат отправляется клиенту:
<div>
<span>By: The React Team</span>
<p>React 19 is...</p>
</div> Компоненты сервера могут быть сделаны динамичными путём повторного извлечения их с сервера, где они могут получить доступ к данным и снова отобразиться. Эта новая архитектура приложения объединяет простую «модель запроса/ответа» серверно-ориентированных многостраничных приложений с плавной интерактивностью клиентно-ориентированных одностраничных приложений, предоставляя вам лучшее из обоих миров.
Добавление интерактивности в компоненты сервера
Компоненты сервера не отправляются в браузер, поэтому они не могут использовать интерактивные API, такие как useState. Чтобы добавить интерактивность в компоненты сервера, вы можете комбинировать их с клиентскими компонентами, используя директиву "use client".
Примечание
Нет директивы для компонентов сервера.
Распространённое заблуждение заключается в том, что компоненты сервера обозначаются "use server", но директивы для компонентов сервера нет. Директива "use server" используется для серверных функций.
Дополнительную информацию см. в документации по Директивам.
В следующем примере Notes компонент сервера импортирует Expandable клиентский компонент, который использует состояние для переключения своего expanded состояния:
// Server Component
import Expandable from './Expandable';
async function Notes() {
const notes = await db.notes.getAll();
return (
<div>
{notes.map(note => (
<Expandable key={note.id}>
<p note={note} />
</Expandable>
))}
</div>
)
} // Client Component
"use client"
export default function Expandable({children}) {
const [expanded, setExpanded] = useState(false);
return (
<div>
<button
onClick={() => setExpanded(!expanded)}
>
Toggle
</button>
{expanded && children}
</div>
)
} Это работает, сначала отобразив Notes как компонент сервера, а затем указав сборщику создать сборку для клиентского компонента Expandable. В браузере клиентские компоненты увидят вывод компонентов сервера, переданных в качестве свойств:
<head>
<!-- the bundle for Client Components -->
<script src="bundle.js" />
</head>
<body>
<div>
<Expandable key={1}>
<p>this is the first note</p>
</Expandable>
<Expandable key={2}>
<p>this is the second note</p>
</Expandable>
<!--...-->
</div>
</body> Асинхронные компоненты с компонентами сервера
Компоненты сервера представляют новый способ написания компонентов с использованием async/await. Когда вы await в асинхронном компоненте, React приостановит выполнение и дождётся разрешения обещания, прежде чем продолжить рендеринг. Это работает на границах сервера/клиента с поддержкой потоковой передачи для Suspense.
Вы даже можете создать обещание на сервере и дождаться его на клиенте:
// Server Component
import db from './database';
async function Page({id}) {
// Will suspend the Server Component.
const note = await db.notes.get(id);
// NOTE: not awaited, will start here and await on the client.
const commentsPromise = db.comments.get(note.id);
return (
<div>
{note}
<Suspense fallback={<p>Loading Comments...</p>}>
<Comments commentsPromise={commentsPromise} />
</Suspense>
</div>
);
} // Client Component
"use client";
import {use} from 'react';
function Comments({commentsPromise}) {
// NOTE: this will resume the promise from the server.
// It will suspend until the data is available.
const comments = use(commentsPromise);
return comments.map(commment => <p>{comment}</p>);
} Контент note важен для отображения страницы, поэтому мы await его на сервере. Комментарии расположены под складкой и имеют меньший приоритет, поэтому мы запускаем обещание на сервере и ждём его на клиенте с помощью use API. Это приостановит выполнение на клиенте, не блокируя отображение note контента.
Поскольку асинхронные компоненты не поддерживаются на клиенте, мы ждём обещание с use.
© 2013–present Facebook Inc.
Licensed under the Creative Commons Attribution 4.0 International Public License.
https://react.dev/reference/rsc/server-components