Введение
Рендеринг на стороне сервера (SSR) — это популярная методика рендеринга одностраничного клиентского приложения (SPA) на сервере и последующей отправки на клиент полностью отрисованной страницы. Это позволяет использовать динамические компоненты в качестве статической разметки HTML.
Такой подход может быть полезным для поисковой оптимизации (SEO), когда при индексации код JavaScript не обрабатывается надлежащим образом. Это также может быть полезно в ситуациях, когда загрузка большого блока JavaScript затруднена из-за медленной скорости сети.
В этом учебном модуле мы инициализируем приложение React с помощью Create React App, а затем изменим проект, чтобы он активировал рендеринг на стороне сервера.
После прохождения учебного модуля вы получите работающий проект с клиентским приложением React и серверным приложением Express.
Примечание. Также Next.js позволяет использовать современный подход к созданию статических приложений React и приложений, рендеринг которых выполняется на сервере.
Предварительные требования
Для данного обучающего руководства вам потребуется следующее:
- Локальная установка Node.js, выполненная согласно указаниям учебного модуля Установка Node.js и создание локальной среды разработки.
Этот учебный модуль был проверен с версиями Node v14.4.0 и npm
v6.14.5.
Шаг 1 — Создание приложения React и изменение компонента приложения
Вначале мы используем npx для запуска нового приложения React с помощью последней версии Create React App.
Назовем наше приложение my-ssr-app:
- npx [email protected] my-ssr-app
Перейдем в новый каталог с помощью команды cd
:
cd my-ssr-app
В заключение мы запустим наше новое приложение на стороне клиента для проверки установки:
- npm start
Вы должны увидеть пример приложения React в окне браузера.
Теперь создадим компонент <Home>
:
- nano src/Home.js
Затем добавим следующий код в файл Home.js
:
src/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
Затем заменим существующие строки кода новыми строками кода:
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
следующим кодом:
index.js
import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; ReactDOM.hydrate(<App />, document.getElementById('root'));
Мы завершили настройку на стороне клиента и теперь можем перейти к настройке на стороне сервера.
Шаг 2 — Создание сервера Express и рендеринг компонента приложения
Теперь наше приложение готово, и мы настроим сервер, который будет отправлять готовую версию после рендеринга. Для сервера мы будем использовать Express. Добавим его в проект, введя следующую команду в окне терминала:
- npm install [email protected]
Также можно использовать yarn:
- yarn add [email protected]
Создайте каталог server
рядом с каталогом src
нашего приложения:
- mkdir server
Затем создайте новый файл index.js
, содержащий код сервера Express:
- nano server/index.js
Добавим необходимые элементы импорта и определим некоторые константы:
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();
Затем добавим код сервера с обработкой ошибок:
server/index.js
// ... 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>
из клиентского приложения непосредственно с сервера.
Здесь происходит три важные вещи:
- Мы предписываем Express вывести содержимое каталога
build
в виде статичных файлов. - Мы будем использовать метод
ReactDOMServer
,renderToString
, для рендеринга нашего приложения в статичную строку HTML. - Затем мы считываем статичный файл
index.html
из готового клиентского приложения, вставляем статичное содержимое нашего приложения в<div>
сid
"root"
, а затем отправляем результат в качестве ответа на запрос.
Шаг 3 — Настройка webpack, Babel и скриптов npm
Чтобы наш серверный код работал, нам нужно объединить его в комплект и провести транспиляцию, используя webpack и Babel. Для этого добавим в проект зависимости dev, введя следующую команду в окне терминала:
- npm install [email protected] [email protected] [email protected] @babel/[email protected] [email protected] @babel/preset-e[email protected] @babel/[email protected] --save-dev
Также можно использовать yarn:
- yarn add [email protected] [email protected] [email protected] @babel/[email protected] [email protected] @babel/[email protected] @babel/[email protected] --dev
Примечание. В более ранней версии этого учебного модуля мы устанавливали babel-core
, babel-preset-env
и babel-preset-react-app
. Эти пакеты с тех пор были архивированы, и вместо них используются версии с одним репозиторием.
Далее мы создадим файл конфигурации Babel:
- nano .babelrc.json
После этого добавьте готовые настройки env
и react-app
:
.babelrc.json
{ "presets": [ "@babel/preset-env", "@babel/preset-react" ] }
Примечание. В более ранней версии этого учебного модуля мы использовали файл .babelrc
(без расширения .json
). Это был файл конфигурации Babel 6, однако для Babel 7 он больше не используется.
Теперь мы создадим конфигурацию webpack для сервера, который использует Babel Loader для транспиляции кода. Начните с создания файла:
- nano webpack.server.js
Затем добавьте следующие конфигурации в файл 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:
package.json
"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 [email protected] [email protected] --save-dev
Также можно использовать yarn:
- yarn add [email protected] [email protected] --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, где вы найдете упражнения и программные проекты.