Не для слабонервных говорю вам.

Но если вас интересуют преимущества SSR (а именно SEO и более быстрая загрузка), и вы не хотите полностью обновлять свой код (потому что вы это сделаете), чтобы получить такие вещи, как окно, работа с документами (Node не знает о них , сюрприз!) и даже более простых шаблонов, таких как async await (шок!), работающих с babel, вот вам небольшой совет.

Идея SSR такова:

  • Вам не нужно приложение только на JS. Вам нужен HTML-код, чтобы сканеры могли понять, о чем ваш веб-сайт, а именно о статическом содержании.
  • Когда браузер запрашивает страницу с вашего веб-сайта (GET / home), ваш сервер должен вернуть некоторый html
  • Последующие взаимодействия со страницей (если вы не перезагружаете / не открываете другую страницу в новой вкладке) обрабатываются через JS (bundle.js в React).

Выглядит это примерно так:

import Express from 'express';
import React from 'react';
import {createStore} from 'redux';
import {Provider} from 'react-redux';
import {reducerFn} from './reducers';
import {renderToString} from 'react-dom/server';
import {routes} from './app';
import {StaticRouter} from "react-router-dom";
import {Helmet} from "react-helmet";


const app = Express();
const port = 8092;

// Serve static files
app.use("/assets", Express.static('assets'));
app.use("/static", Express.static('static'));

// This is fired every time the server side receives a request
app.use(handleRender);


// We are going to fill these out in the sections to follow
function handleRender(req, res) {
    const fullUrl = req.protocol + '://' + req.get('host') + req.originalUrl;
    console.log('fullUrl: ', fullUrl);
    console.log('req.url: ', req.url);

    // Create a new Redux store instance
    const store = createStore(reducerFn);

    // Render the component to a string
    const html = renderToString(
        <Provider store={store}>
            <StaticRouter location={req.url} context={{}}>
                {routes}
            </StaticRouter>
        </Provider>
    );
    const helmet = Helmet.renderStatic();

    // Grab the initial state from our Redux store
    const preloadedState = store.getState();

    // Send the rendered page back to the client
    res.send(renderFullPage(helmet, html, preloadedState));
}

function renderFullPage(helmet, html, preloadedState) {
    return `
    <!doctype html>
    <html>
      <head>
        ${helmet.title.toString()}
        ${helmet.meta.toString()}
        ${helmet.link.toString()}
        ${helmet.style.toString()}
        ${helmet.script.toString()}
        
        <link rel="manifest" href="%PUBLIC_URL%/manifest.json">
        <link rel="icon" href="https://imageserver.homedruid.com/v1/image/get?id=1398" type="image/png" />

        <script type="text/javascript" async
                src="https://maps.googleapis.com/maps/api/js?key=AIzaSyB_6Ly7ovRtYqb_p7QxxRSV3WnQh3b1e6Y&libraries=places"></script>    
      </head>
      <body style="margin: 0">
        <div id="root">${html}</div>\n
        <script>
          // WARNING: See the following for security issues around embedding JSON in HTML:
          // http://redux.js.org/recipes/ServerRendering.html#security-considerations
          window.__PRELOADED_STATE__ = ${JSON.stringify(preloadedState).replace(/</g, '\\u003c')}
        </script>
        <script src="/assets/bundle.js"></script>
      </body>
    </html>
    `;
}


app.listen(port);

Посмотрите на магию ниже:

// Magic 1
renderToString()
// Magic 2
<script src="/assets/bundle.js"></script>

Мы используем Node в качестве браузера для выполнения JS, рендеринга макета в виде html и его обслуживания. Мы также отправляем код bundle.js (наш SPA), который будет перезаписывать визуализированный макет html и снова отображать компоненты в JS. Не думал об этом, когда начинал, но это довольно просто и эффективно. Основная проблема в SSR заключается в том, чтобы заставить работать эти два волшебных элемента.

Итак, вот хитрость. Если вы не можете заставить SSR работать, потому что это слишком болезненно, сделайте следующее. Напишите простой сервер nginx / Express, который обслуживает статический html (вы можете получить его, отрисовав SPA и получив document.innerHTML) для домашней страницы вместе с bundle.js.

И это все !

Хотите увидеть это в действии: https://www.heloprotocol.in/