Рендеринг на стороне сервера (SSR) — это популярная методика рендеринга одностраничного клиентского приложения (SPA) на сервере и последующей отправки на клиент полностью отрисованной страницы. Это позволяет использовать динамические компоненты в качестве статической разметки HTML.
Такой подход может быть полезным для поисковой оптимизации (SEO), когда при индексации код JavaScript не обрабатывается надлежащим образом. Это также может быть полезно в ситуациях, когда загрузка большого блока JavaScript затруднена из-за медленной скорости сети.
В этом учебном модуле мы инициализируем приложение React с помощью Create React App, а затем изменим проект, чтобы он активировал рендеринг на стороне сервера.
После прохождения учебного модуля вы получите работающий проект с клиентским приложением React и серверным приложением Express.
Примечание. Также Next.js позволяет использовать современный подход к созданию статических приложений React и приложений, рендеринг которых выполняется на сервере.
Для данного обучающего руководства вам потребуется следующее:
Этот учебный модуль был проверен с версиями Node v14.4.0 и npm
v6.14.5.
Вначале мы используем npx для запуска нового приложения React с помощью последней версии Create React App.
Назовем наше приложение my-ssr-app:
- npx create-react-app@3.4.1 my-ssr-app
Перейдем в новый каталог с помощью команды cd
:
cd my-ssr-app
В заключение мы запустим наше новое приложение на стороне клиента для проверки установки:
- npm start
Вы должны увидеть пример приложения React в окне браузера.
Теперь создадим компонент <Home>
:
- nano src/Home.js
Затем добавим следующий код в файл Home.js
:
import React from 'react';
export default props => {
return <h1>Hello {props.name}!</h1>;
};
При этом будет создан заголовок <h1>
с сообщением "Hello
", адресованным имени.
Далее мы выполним рендеринг <Home>
в компоненте <App>
. Откройте файл App.js
:
- nano src/App.js
Затем заменим существующие строки кода новыми строками кода:
import React from 'react';
import Home from './Home';
export default () => {
return <Home name="Sammy" />;
};
Они передают name
в компонент <Home>
так, что ожидаемое сообщение будет выглядеть так: "Hello Sammy!"
.
В файле index.js
нашего приложения мы будем использовать метод ReactDOM hydrate
вместо render
, чтобы указать блоку рендеринга DOM, чтобы мы восстанавливаем приложение после рендеринга на стороне сервера.
Откроем файл index.js
:
- nano index.js
Замените содержимое файла index.js
следующим кодом:
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.hydrate(<App />, document.getElementById('root'));
Мы завершили настройку на стороне клиента и теперь можем перейти к настройке на стороне сервера.
Теперь наше приложение готово, и мы настроим сервер, который будет отправлять готовую версию после рендеринга. Для сервера мы будем использовать Express. Добавим его в проект, введя следующую команду в окне терминала:
- npm install express@4.17.1
Также можно использовать yarn:
- yarn add express@4.17.1
Создайте каталог server
рядом с каталогом src
нашего приложения:
- mkdir server
Затем создайте новый файл index.js
, содержащий код сервера Express:
- nano server/index.js
Добавим необходимые элементы импорта и определим некоторые константы:
import path from 'path';
import fs from 'fs';
import React from 'react';
import express from 'express';
import ReactDOMServer from 'react-dom/server';
import App from '../src/App';
const PORT = process.env.PORT || 3006;
const app = express();
Затем добавим код сервера с обработкой ошибок:
// ...
app.get('/', (req, res) => {
const app = ReactDOMServer.renderToString(<App />);
const indexFile = path.resolve('./build/index.html');
fs.readFile(indexFile, 'utf8', (err, data) => {
if (err) {
console.error('Something went wrong:', err);
return res.status(500).send('Oops, better luck next time!');
}
return res.send(
data.replace('<div id="root"></div>', `<div id="root">${app}</div>`)
);
});
});
app.use(express.static('./build'));
app.listen(PORT, () => {
console.log(`Server is listening on port ${PORT}`);
});
Как видите, мы можем импортировать наш компонент <App>
из клиентского приложения непосредственно с сервера.
Здесь происходит три важные вещи:
build
в виде статичных файлов.ReactDOMServer
, renderToString
, для рендеринга нашего приложения в статичную строку HTML.index.html
из готового клиентского приложения, вставляем статичное содержимое нашего приложения в <div>
с id
"root"
, а затем отправляем результат в качестве ответа на запрос.npm
Чтобы наш серверный код работал, нам нужно объединить его в комплект и провести транспиляцию, используя webpack и Babel. Для этого добавим в проект зависимости dev, введя следующую команду в окне терминала:
- npm install webpack@4.42.0 webpack-cli@3.3.12 webpack-node-externals@1.7.2 @babel/core@7.10.4 babel-loader@8.1.0 @babel/preset-env@7.10.4 @babel/preset-react@7.10.4 --save-dev
Также можно использовать yarn:
- yarn add webpack@4.42.0 webpack-cli@3.3.12 webpack-node-externals@1.7.2 @babel/core@7.10.4 babel-loader@8.1.0 @babel/preset-env@7.10.4 @babel/preset-react@7.10.4 --dev
Примечание. В более ранней версии этого учебного модуля мы устанавливали babel-core
, babel-preset-env
и babel-preset-react-app
. Эти пакеты с тех пор были архивированы, и вместо них используются версии с одним репозиторием.
Далее мы создадим файл конфигурации Babel:
- nano .babelrc.json
После этого добавьте готовые настройки env
и react-app
:
{
"presets": [
"@babel/preset-env",
"@babel/preset-react"
]
}
Примечание. В более ранней версии этого учебного модуля мы использовали файл .babelrc
(без расширения .json
). Это был файл конфигурации Babel 6, однако для Babel 7 он больше не используется.
Теперь мы создадим конфигурацию webpack для сервера, который использует Babel Loader для транспиляции кода. Начните с создания файла:
- nano webpack.server.js
Затем добавьте следующие конфигурации в файл webpack.server.js
:
const path = require('path');
const nodeExternals = require('webpack-node-externals');
module.exports = {
entry: './server/index.js',
target: 'node',
externals: [nodeExternals()],
output: {
path: path.resolve('server-build'),
filename: 'index.js'
},
module: {
rules: [
{
test: /\.js$/,
use: 'babel-loader'
}
]
}
};
С этой конфигурацией наш транспилированный серверный комплект будет выводиться в папку server-build
в файле с именем called index.js
.
Обратите внимание на использование target: 'node'
и externals: [nodeExternals()]
из webpack-node-externals
. При этом опускаются файлы из node_modules
в комплекте, сервер сможет получить доступ к этим файлам напрямую.
Это завершает установку зависимости и конфигурации webpack и Babel.
Теперь мы снова вернемся к файлу package.json
и добавим вспомогательные скрипты npm
:
- nano package.json
Мы добавим скрипты dev:build-server
, dev:start
и dev
в файл package.json
, чтобы легко выполнять сборку и подачу нашего приложения SSR:
"scripts": {
"dev:build-server": "NODE_ENV=development webpack --config webpack.server.js --mode=development -w",
"dev:start": "nodemon ./server-build/index.js",
"dev": "npm-run-all --parallel build dev:*",
...
},
Мы используем nodemon
для перезапуска сервера при внесении изменений. Также мы используем npm-run-all
для параллельного выполнения нескольких команд.
Давайте установим эти пакеты, введя следующие команды в окне терминала:
- npm install nodemon@2.0.4 npm-run-all@4.1.5 --save-dev
Также можно использовать yarn:
- yarn add nodemon@2.0.4 npm-run-all@4.1.5 --dev
Так вы можете запустить следующий код для сборки приложения на стороне клиента, объединения в пакет и транспиляции кода сервера и запуска сервера на порту :3006
:
- npm run dev
Также можно использовать yarn:
- yarn run dev
Наша конфигурация сервера webpack будет следить за изменениями, и в случае изменений наш сервер перезапустится. Однако для клиентского приложения нам нужно выполнять сборку каждый раз при внесении изменений. Для этого есть открытая проблема.
Откройте в браузере адрес http://localhost:3006/
и вы увидите приложение после рендеринга на стороне сервера.
Ранее исходный код показал следующее:
Output<div id="root"></div>
С внесенными изменениями исходный код показывает:
Output<div id="root"><h1 data-reactroot="">Hello <!-- -->Sammy<!-- -->!</h1></div>
При рендеринге на стороне сервера компонент <App>
был успешно конвертирован в формат HTML.
В этом учебном модуле мы инициализировали приложение React и активировали рендеринг на стороне сервера.
Так мы просто оценили доступные возможности. Все становится сложнее, если в приложение после рендеринга на стороне сервера нужно добавить маршрутизацию, доставку данных или Redux.
Преимущество использования SSR заключается в наличии приложения, содержимое которого может просмотреть даже сборщик, не выполняющий код JavaScript. Это поможет для поисковой оптимизации (SEO) и отправки метаданных на каналы социальных сетей.
SSR также часто помогает решить проблемы с производительностью, потому что полностью загруженное приложение отправляется с сервера при первом запросе. С необычными приложениями ситуация может меняться, потому что SSR требует довольно сложной настройки и создает более существенную нагрузку на сервер. Использование рендеринга на стороне сервера для приложения React зависит от конкретных потребностей и от того, какие компромиссы имеют смысл для вашего сценария использования.
Если вы хотите узнать больше о React, почитайте нашу серию «Программирование на React.js» или посмотрите страницу тем React, где вы найдете упражнения и программные проекты.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!
Пример хороший и рабочий, но на реальном приложении не работает, запустить можно. Кому лень вот репозиторий - https://github.com/Octanium91/react-app-ssr