Одностраничные приложения (SPA) — это круто. Одним из больших преимуществ, которые предлагают SPA, является устранение необходимости перезагрузки страницы для увеличения скорости и UX. Процесс разработки также прост и оптимизирован. Еще одним преимуществом является то, что SPA относительно просто отлаживать с помощью Chrome, и они даже могут эффективно кэшировать локальные хранилища. Чтобы представить популярность SPA в перспективе, современные приложения, такие как Gmail, Google Maps, Facebook или GitHub, являются SPA.

Но есть одна загвоздка. Первоначальный запрос GET, отправленный на сервер для извлечения веб-приложения, скорее всего, возвращает пустой файл HTML с дополнительными внешними файлами (код CSS и JavaScript), которые необходимо загрузить для отображения соответствующей разметки. Если вы используете React.js с Webpack, вы можете себе представить огромный пакет JSX, который также необходимо обработать. Это означает, что, особенно для больших SPA, пользователю придется дольше ждать начального рендеринга страницы, что снижает производительность сети, а поисковые роботы могут интерпретировать вашу веб-страницу как пустую, тем самым снижая ваш SEO-рейтинг (это означает, что ваш сайт будет иметь тяжело пытаться занять верхние позиции в результатах поиска).

Вот где в игру вступает рендеринг на стороне сервера (SSR). Если ваше приложение изначально «предварительно отрендерено» на сервере, пользователь получит полностью отрендеренное приложение, как только первоначальный запрос GET вернет ответ. Это позволяет практически мгновенно загружать статические компоненты веб-приложения на клиенте. Кроме того, это отличная новость для вашего SEO-рейтинга, поскольку поисковые роботы теперь будут видеть ваш веб-сайт, как и любой другой статический сайт в Интернете, и будут индексировать весь контент, который вы отображаете на сервере. С учетом всего сказанного, давайте начнем!

Установка

**Примечание. В этом руководстве будет использоваться Node.js с Express в качестве внутреннего сервера, Webpack в качестве сборщика и Babel в качестве транспилятора.

  1. Установите зависимости
$ npm install --save-dev @babel/cli @babel/core @babel/node @babel/preset-env @babel/preset-react babel-loader webpack webpack-cli
$ npm install --save express react react-dom

Для удобства мы будем использовать Webpack для объединения нашего кода React и использовать Babel для переноса синтаксиса JSX и ES6 в удобочитаемый JavaScript ES5 (это обеспечивает совместимость синтаксиса между всеми браузерами, а также с вашим сервером Node).

2.Настройте файлы конфигурации

// .babelrc file
{
  "presets": ["@babel/preset-env", "@babel/preset-react"]
}
// webpack.config.js file
const path = require('path');
const webpack = require('webpack');
module.exports = {
  entry: '/path/to/React/Index/Page',
  output: {
    filename: 'w/e you want to call your bundle'
    path: path.resolve(where you want your bundle to be located)
  },
  module: {
    rules: [
      {
        test: [/\.jsx?$/],
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env', '@babel/preset-react']
          }
        }
      }
    ]
  }
};

3. Создайте сервер.

** В этом руководстве предполагается, что вы уже создали свои компоненты React.

import express from 'express';
import path from 'path';
import React from 'react';
import { renderToString } from 'react-dom/server';
import YourReactApp from 'path/to/your/main/React/App/Component';
const app = express();
// Serve up your webpack bundle / CSS file as static assets
app.use(express.static(path.join(__dirname, '../path/to/bundle')));
app.listen(3000, () => {
  console.log('Server is now listening on port 3000');
});

4.Создайте свой HTML и компоненты React на стороне сервера

// Within your Node Server
const htmlTemplate = ReactComponents => {
  return `
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial- 
         scale=1.0" />
        <meta http-equiv="X-UA-Compatible" content="ie=edge" />
        <meta name="Description" content="Practice App for server  
         side rendering">
        <title>SSR Practice</title>
      </head>
      <body>
        <div id="app">${ReactComponents}</div>
        <script src="YourWebpackBundle.js"></script>
      </body>
    </html>
  `;
};
app.get('*', (req, res) => {
  const ReactComponent = renderToString(<YourReactApp />);
  res.writeHead(200, { 'Content-Type': 'text/html' });
  res.end(htmlTemplate(ReactComponent));
});

На этом этапе происходит несколько важных вещей, поэтому давайте разберем их по порядку. Во-первых, мы создаем функцию, которая будет возвращать строковую версию вашего HTML-файла. Мы также настроили его так, чтобы он принимал один аргумент, который мы будем использовать для вставки наших компонентов React. Во-вторых, мы настроили маршрут GET для ответа нашим статическим SPA — вместе с заголовком, чтобы сообщить клиенту, что файл имеет HTML-содержимое. Функция renderToString() — это метод из ReactDOM/Server, и мы используем его для рендеринга наших компонентов React в статическую строку HTML. Думайте об этом как оJSON.stringify() для React, если хотите.

5. Запустите свой сервер

$ node --exec babel-node server/index.js

Убедитесь, что вы запускаете свой сервер Node с помощью Babel-Node! В противном случае весь синтаксис JSX и ES6 приведет к сбою сервера.

Теперь, в этот момент, ваше приложение будет обслуживаться посредством рендеринга на стороне сервера! …Но есть еще один важный шаг, который необходимо реализовать. Ваше приложение в настоящее время «безжизненно», оно представляет собой чисто статическую разметку и, следовательно, может отображать только компоненты вашего приложения. Итак, каков последний шаг?

6. Расширьте возможности своего приложения

// Index.jsx
import React from 'react';
import ReactDOM from 'react-dom';
import YourReactApp from './components/app.jsx';
ReactDOM.hydrate(<YourReactApp />, document.getElementById('app'));

Отличные разработчики из команды React придумали метод ReactDOM.hydrate() для оптимизации SSR при его реализации с помощью React. Если вы вызываете hydr на узле, который уже имеет разметку, визуализируемую сервером, React сохранит ее и прикрепит только обработчики событий, что позволит вам получить очень эффективную первую загрузку! Проще говоря, гидрат в основном возвращает ваше приложение к жизни. Думайте об этом, как о поливе засохшего растения и оживлении его! Таким образом, прикрепив наш пакет веб-пакетов к файлу HTML в виде скрипта, он будет запускать метод гидрата при загрузке на клиенте. Обычно для рендеринга на стороне клиента мы используем ReactDOM.render() для загрузки нашего приложения. Чтобы быть более конкретным, этот метод — в отличие от гидратации — фактически изменяет ваш узел, если есть какая-либо разница между исходным DOM и текущим DOM. Это похоже на замену увядшего растения новым растением. По сути, использование рендеринга в этом случае было бы контрпродуктивным по сравнению с тем, что мы пытаемся достичь с помощью SSR.

Заключительные замечания

Поздравляем! Если вы зашли так далеко, вы только что полностью реализовали рендеринг на стороне сервера с помощью React. Однако я хочу кратко упомянуть, что, хотя SSR часто может помочь с веб-производительностью… это не всегда может быть лучшим вариантом. Например, в случае нетривиальных приложений это может быть скорее обременительно, чем полезно, поскольку SSR требует настройки, которая может немного усложниться, а также создает большую нагрузку на ваш сервер Node. В общем, вам решать, использовать ли SSR для вашего приложения React, учитывая ваши конкретные потребности, и определить, какие компромиссы наиболее целесообразны для вашего варианта использования.

Вот и все! Спасибо за чтение, я надеюсь, что этот урок был полезен.