// Tutorial //

Включение рендеринга на стороне сервера для приложения React

Published on November 23, 2020
Default avatar
By Alligator.io
Developer and author at DigitalOcean.
Русский
Включение рендеринга на стороне сервера для приложения React

Введение

Рендеринг на стороне сервера (SSR) — это популярная методика рендеринга одностраничного клиентского приложения (SPA) на сервере и последующей отправки на клиент полностью отрисованной страницы. Это позволяет использовать динамические компоненты в качестве статической разметки HTML.

Такой подход может быть полезным для поисковой оптимизации (SEO), когда при индексации код JavaScript не обрабатывается надлежащим образом. Это также может быть полезно в ситуациях, когда загрузка большого блока JavaScript затруднена из-за медленной скорости сети.

В этом учебном модуле мы инициализируем приложение React с помощью Create React App, а затем изменим проект, чтобы он активировал рендеринг на стороне сервера.

После прохождения учебного модуля вы получите работающий проект с клиентским приложением React и серверным приложением Express.

Примечание. Также Next.js позволяет использовать современный подход к созданию статических приложений React и приложений, рендеринг которых выполняется на сервере.

Предварительные требования

Для данного обучающего руководства вам потребуется следующее:

Этот учебный модуль был проверен с версиями Node v14.4.0 и npm v6.14.5.

Шаг 1 — Создание приложения React и изменение компонента приложения

Вначале мы используем npx для запуска нового приложения React с помощью последней версии Create React App.

Назовем наше приложение my-ssr-app:

  1. npx create-react-app@3.4.1 my-ssr-app

Перейдем в новый каталог с помощью команды cd:

cd my-ssr-app

В заключение мы запустим наше новое приложение на стороне клиента для проверки установки:

  1. npm start

Вы должны увидеть пример приложения React в окне браузера.

Теперь создадим компонент <Home>:

  1. 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:

  1. 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:

  1. 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. Добавим его в проект, введя следующую команду в окне терминала:

  1. npm install express@4.17.1

Также можно использовать yarn:

  1. yarn add express@4.17.1

Создайте каталог server рядом с каталогом src нашего приложения:

  1. mkdir server

Затем создайте новый файл index.js, содержащий код сервера Express:

  1. 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, введя следующую команду в окне терминала:

  1. 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:

  1. 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:

  1. nano .babelrc.json

После этого добавьте готовые настройки env и react-app:

.babelrc.json
{
  "presets": [
    "@babel/preset-env",
    "@babel/preset-react"
  ]
}

Примечание. В более ранней версии этого учебного модуля мы использовали файл .babelrc (без расширения .json). Это был файл конфигурации Babel 6, однако для Babel 7 он больше не используется.

Теперь мы создадим конфигурацию webpack для сервера, который использует Babel Loader для транспиляции кода. Начните с создания файла:

  1. 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:

  1. 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 для параллельного выполнения нескольких команд.

Давайте установим эти пакеты, введя следующие команды в окне терминала:

  1. npm install nodemon@2.0.4 npm-run-all@4.1.5 --save-dev

Также можно использовать yarn:

  1. yarn add nodemon@2.0.4 npm-run-all@4.1.5 --dev

Так вы можете запустить следующий код для сборки приложения на стороне клиента, объединения в пакет и транспиляции кода сервера и запуска сервера на порту :3006:

  1. npm run dev

Также можно использовать yarn:

  1. 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, где вы найдете упражнения и программные проекты.


Want to learn more? Join the DigitalOcean Community!

Join our DigitalOcean community of over a million developers for free! Get help and share knowledge in our Questions & Answers section, find tutorials and tools that will help you grow as a developer and scale your project or business, and subscribe to topics of interest.

Sign up
About the authors
Default avatar
Developer and author at DigitalOcean.

Default avatar
Developer and author at DigitalOcean.

Still looking for an answer?

Was this helpful?
1 Comments

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