35

我正在尝试使用 react-router 4 进行服务器端渲染。我正在遵循此处提供的示例https://reacttraining.com/react-router/web/guides/server-rendering/putting-it-all-together

根据服务器上的示例,我们应该使用StaticRouter. 当我按照示例导入时,我看到 StaticRouter 未定义

import {StaticRouter} from 'react-router';

在网上做了一些研究后,我发现我可以使用react-router-dom. 现在我的导入语句看起来像这样。

import {StaticRouter} from 'react-router-dom';

但是,当我运行代码时,我正在Invariant Violation: Browser history needs a DOM浏览器中。

我的 server.js 文件代码

....
app.get( '*', ( req, res ) => {
  const html = fs.readFileSync(path.resolve(__dirname, '../index.html')).toString();
  const context = {};
  const markup = ReactDOMServer.renderToString(
    <StaticRouter location={req.url} context={context} >
      <App/>
    </StaticRouter>
  );

  if (context.url) {
    res.writeHead(302, {
      Location: context.url
    })
    res.end();
  } else {
      res.send(html.replace('$react', markup));
  }
} );
....

还有我的 client/index.js 代码

....
ReactDOM.render((
  <BrowserRouter>
    <App />
  </BrowserRouter>
), root);
....

更新 v1 将我的示例减少到最低限度,但仍然出现相同的错误。

客户端索引.js

import ReactDOM from 'react-dom'
import { BrowserRouter } from 'react-router-dom'
import App from '../App'

ReactDOM.render((
  <BrowserRouter>
    <App/>
  </BrowserRouter>
), document.getElementById('app'))

服务器索引.js

import { createServer } from 'http'
import React from 'react'
import ReactDOMServer from 'react-dom/server'
import { StaticRouter } from 'react-router'
import App from '../App'

createServer((req, res) => {
  const context = {}

  const html = ReactDOMServer.renderToString(
    <StaticRouter
      location={req.url}
      context={context}
    >
      <App/>
    </StaticRouter>
  )

res.write(`
  <!doctype html>
  <div id="app">${html}</div>
`)
res.end()
}).listen(3000);

应用程序.js

import React from 'react';
import { BrowserRouter as Router, Route } from 'react-router-dom';
import routes from "./client/routes";
const App = ( ) => (
  <Router>
    <Route path="/" exact render={( props ) => ( <div>Helloworld</div> )} />
  </Router>
)

export default App;
4

3 回答 3

28

您需要使用不同的历史提供程序进行服务器端渲染,因为您在服务器上没有真正的 DOM(和浏览器的历史)。因此,将 BrowserRouter 替换为 Router 和 app.js 中的备用历史提供程序可以解决问题。此外,您不必使用两个包装器。您在 app.js 和 clientIndex.js 中使用了两次 BrowserRouter,这是不必要的。

import { Route, Router } from 'react-router-dom';
import { createMemoryHistory } from 'history';

const history = createMemoryHistory();

  <Router history={history}>
   <Route path="/" exact render={( props ) => ( <div>Helloworld</div> )} />
  </Router>

您现在可以用可在客户端和服务器中使用的 ConnectedRouter 替换 StaticRouter。我使用以下代码在历史记录之间进行选择并将其导出以用于 ConnectedRouter 的历史记录。

export default (url = '/') => {
// Create a history depending on the environment
  const history = isServer
    ? createMemoryHistory({
        initialEntries: [url]
     })
   : createBrowserHistory();
}
于 2018-07-25T06:13:08.510 回答
12

在 clientIndex.js

而不是 BrowserRouter 使用 StaticRouter。

从 'react-router-dom' 导入 { BrowserRouter };

从 'react-router-dom' 导入 { StaticRouter }

于 2020-01-11T22:38:32.263 回答
5

正如评论中所指出的那样,一个人可能会通过意外地将您的 App 组件包装在 a 中而遇到此错误(就像我一样) ,而应该包装的<BrowserRouter>是您的客户端应用程序。

应用程序.js

import React from 'react'

const App = () => <h1>Hello, World.</h1>

export default App

ClientApp.js

import React from 'react'
import { BrowserRouter } from 'react-router-dom'
import ReactDOM from 'react-dom'
import App from './App'

const render = Component => {
  ReactDOM.render(
    <BrowserRouter>
      <Component />
    </BrowserRouter>,
    document.getElementById('app')
  )
}

render(App)

另请参阅React Router 文档

于 2017-07-11T19:52:24.663 回答