Рендеринг на стороне сервера с помощью маршрутизатора React «Истории браузера нужен DOM»

#node.js #reactjs #react-router #create-react-app #server-side-rendering

#node.js #reactjs #react-маршрутизатор #create-react-app #рендеринг на стороне сервера

Вопрос:

Я просматривал Интернет в течение двух дней, но не могу понять суть реализации SSR с помощью react-routes. Мне потребовалось 5 дней, чтобы как-то разобраться во всем этом без маршрутов. Но теперь я не могу дальше разбираться с использованием react-routes. Вот необходимые детали-

server/server.js

 import express from 'express';
import fs from 'fs';
import path from 'path';

import React from 'react';
import ReactDOMServer from 'react-dom/server';
import {StaticRouter} from 'react-router-dom';

import App from '../src/App';

const PORT = process.env.PORT || 8080;

const app = express();

app.use(express.static(path.resolve('./build')))

app.get('*', (req, res) => {

    fs.readFile(path.resolve('./build/index.html'), 'utf-8', (err, data) => {

    if (err) {
      console.log(err);
      return res.status(500).send('Some error happened');
        }

        const context = {};
        const app = ReactDOMServer.renderToString(
            <StaticRouter location={req.url} context={context}>
                <App />
            </StaticRouter>
        )
        
    return res.send(
      data.replace(
        '<div id="root"></div>',
        `<div id="root">${app}</div>`
      )
    );
    });
    
});

app.listen(PORT, () => {
  console.log(`App launched on ${PORT}`);
});
  

server/index.js

 require('ignore-styles')

require('@babel/register')({
    ignore: [/(node_module)/],
    presets: ['@babel/preset-env', '@babel/preset-react']
})

require('./server')
  

src/App.js

 import React from "react";
import {BrowserRouter, Switch, Route, Link} from 'react-router-dom';
import "./App.css";

function App() {

  return (
    <BrowserRouter>
      <Switch>
        <Route path='/' exact component={() => <Link to='/about'>This is Home. Go to About.</Link>} />
        <Route path='/about' component={() => <Link to='/'>This is About. Go to Home.</Link>} />
      </Switch>
    </BrowserRouter>
  );
}

export default App;
  

src/index.js

 import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';

ReactDOM.hydrate(<App />, document.getElementById('root'));

serviceWorker.unregister();
  

Ответ №1:

Удалите BrowserRouter из приложения и поместите его в свой индексный файл.

index.js

 ReactDOM.hydrate(
       <BrowserRouter>
          <App />
       </BrowserRouter>, 
   document.getElementById('root')
);
  

App.js

 import React from "react";
import { Switch, Route, Link} from 'react-router-dom';
import "./App.css";

function App() {

  return (
      <Switch>
        <Route path='/' exact component={() => <Link to='/about'>This is Home. Go to About.</Link>} />
        <Route path='/about' component={() => <Link to='/'>This is About. Go to Home.</Link>} />
      </Switch>
  );
}
  

Поскольку <App /> содержит BrowserRouter , и вы используете его на сервере. BrowserRouter не может быть обработан на сервере, поскольку для этого требуется DOM.

Кроме того, импортируйте свой StaticRouter из react-router not react-router-dom .

Вы можете проверить документы здесь

 import { StaticRouter } from 'react-router';
  

Комментарии:

1. В чем разница? Он все еще проходит через hydrate.

2. hydrate Функция не запускается на сервере. Он выполняется на стороне клиента (в браузере).

3. В любом случае он использует dom маршрутизатора react в своем server.js файл, и я думаю, что это проблема.

4. Ооо! Я пропустил это, это тоже одна из проблем.

5. По сути, StaticRouter работает на сервере, а BrowserRouter — на клиенте. Вот и все. Поскольку BrowserRouter неявно получает местоположение из window.location клиента, нам нужно явно передать местоположение StaticRouter. После этого все остается по-прежнему.