使用 React Router 进行服务器端渲染“浏览器历史记录需要一个 DOM”
Posted
技术标签:
【中文标题】使用 React Router 进行服务器端渲染“浏览器历史记录需要一个 DOM”【英文标题】:Server-side rendering with React Router "Browser history needs a DOM" 【发布时间】:2021-01-06 19:48:09 【问题描述】:我已经通过互联网扫描了两天,但我无法理解使用 react-routes 实现 s-s-r 的要点。我花了 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`);
);
服务器/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。
另外,从react-router
而非react-router-dom
导入您的StaticRouter
。
您可以查看文档here
import StaticRouter from 'react-router';
【讨论】:
有什么区别?它仍然会通过水合物。hydrate
函数未在服务器中运行。它在客户端(浏览器)中运行。
无论如何他在他的 server.js 文件中使用 react router dom,我认为这是问题所在。
哦!我错过了,这也是问题之一。
基本上,StaticRouter 在服务器上工作,BrowserRouter 在客户端工作。而已。由于 BrowserRouter 隐式地从客户端的window.location
获取位置,我们需要显式地将位置传递给 StaticRouter。在那之后,一切都一样了。以上是关于使用 React Router 进行服务器端渲染“浏览器历史记录需要一个 DOM”的主要内容,如果未能解决你的问题,请参考以下文章
如何在逻辑上结合 react-router 和 redux 进行客户端和服务器端渲染
React Router v5 伴随着代码拆分和使用服务器端渲染的数据预取