如何在服务器端渲染中使用react-router v4?

时间:2017-06-28 22:59:15

标签: react-router-v4

这个文档非常愚蠢 - https://reacttraining.com/react-router/web/guides/server-rendering。这段代码不起作用!它没有说明如何将react-router v4与服务器端渲染一起使用。即使我没有找到任何好的和小的样板来看它是如何工作的。

请帮忙!

3 个答案:

答案 0 :(得分:0)

我已经使用react-router v4和redux创建了起初使用的建议。它使用redux-saga来处理异步api请求。项目刚刚开始。它正在积极发展。所以欢迎贡献,测试。我愿意接受反馈和建议,以及改进。

https://github.com/gzoreslav/react-redux-saga-universal-application

答案 1 :(得分:0)

我已在您的相关问题中发布了有关如何获取SSR以及数据获取的信息:How to preload data with react-router v4?

我们确实需要更多详细信息,一些代码可能会向我们展示您所拥有的内容,以便我们可以帮助您解决问题。

我也准备了一个锅炉板;展示如何将SSR添加到客户端仅反应项目(和其他技术)。 https://github.com/peter-mouland/react-lego

我遵循了你指出的文件。

<强> Root.jsx

此文件仅在客户端上使用,但Jest / Mocha测试也使用它,这就是为什么让BrowserRouter与StaticRouter保持正确非常重要。

import React from 'react';
import BrowserRouter from 'react-router-dom/BrowserRouter';
import StaticRouter from 'react-router-dom/StaticRouter';
import { Provider } from 'react-redux';

import { makeRoutes } from './routes';
import { isBrowser } from './utils';
import configureStore from './store/configure-store';

// exported to be used in tests
export const Router = isBrowser ? BrowserRouter : StaticRouter;
const store = configureStore(window.__INITIAL_STATE__); // eslint-disable-line

export default (props) => (
  <Provider store={store}>
    <Router {...props} >
      {makeRoutes()}
    </Router>
  </Provider>
);

<强>客户entry.jsx

import React from 'react';
import ReactDOM from 'react-dom';

import './config';
import Root from './app/Root';

try {
  ReactDOM.render(<Root />, document.getElementById('html'));
} catch (err) {
  log('Render error', err);
}

希望这有帮助

答案 2 :(得分:0)

要在服务器端渲染React应用程序中使用React Router工作器,您可以创建一个client/Routes.js文件。

此文件将由客户端和服务器端代码库共享。

因此,您将从通常的样板导入开始:

import React from "react";
import { Route } from "react-router-dom";
import Home from "./components/Home";

接下来,您要创建一个React组件,以设置您在典型的React Router应用程序中将看到的所有路由映射:

import React from "react";
import { Route } from "react-router-dom";
import Home from "./components/Home";

export default () => {
  return (
    <div>
      <Route exact path="/" component={Home} />
    </div>
  );
};

然后,您需要像下面这样将此路由映射导入到client.js文件中:

// Startup point for the client side application
import React from "react";
import ReactDOM from "react-dom";
import Home from "./components/Home";

ReactDOM.hydrate(<Home />, document.querySelector("#root"));

您将像这样导入Routes.js文件:

// Startup point for the client side application
import React from "react";
import ReactDOM from "react-dom";
import Routes from "./Routes";

ReactDOM.hydrate(<Home />, document.querySelector("#root"));

注意,我删除了Home组件,因为我知道有一个Routes映射。

您将需要像这样在客户端上导入BrowserRouter

// Startup point for the client side application
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter } from "react-router-dom";
import Routes from "./Routes";

ReactDOM.hydrate(<Home />, document.querySelector("#root"));

最后一步是确保您在ReactDOM.hydrate()调用中使用该代码,如下所示:

// Startup point for the client side application
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter } from "react-router-dom";
import Routes from "./Routes";

ReactDOM.hydrate(
  <BrowserRouter>
    <Routes />
  </BrowserRouter>,
  document.querySelector("#root")
);

因此,这是客户端上的BrowserRouter,请记住,BrowserRouter在服务器上不起作用,因为它没有地址栏,并且BrowserRouter的编码很难接受地址。

当您进入应用程序时,将在控制台中看到以下警告消息:

  

bundle.js:2161警告:服务器HTML预期包含匹配项    在。

该消息表示服务器生成的HTML与客户端生成的HTML不匹配。它说有一个<div> in <div>

这是指额外的<div>

export default () => {
  return (
    <div>
      <Route exact path="/" component={Home} />
    </div>
  );
};

此内容未在服务器端HTML内显示,仅在客户端上显示。

您可以通过如下配置服务器端来开始纠正该警告:

import React from "react";
import { renderToString } from "react-dom/server";
import { StaticRouter } from "react-router-dom";
import Routes from "../client/Routes";

export default () => {
  const content = renderToString(<Home />);

  return `<html><head><body><div id="root">${content}</div><script src="bundle.js"></script></body></head></html>`;
};

注意,我导入了StaticRouter,我将显示StaticRouter并将所有自定义路线放置在其中,如下所示:

export default () => {
  const content = renderToString(
    <StaticRouter>
      <Routes />
    </StaticRouter>
  );

  return `<html><head><body><div id="root">${content}</div><script src="bundle.js"></script></body></head></html>`;
};

StaticRouter确实具有一个称为context的必需属性,如下所示:

export default () => {
  const content = renderToString(
    <StaticRouter context={{}}>
      <Routes />
    </StaticRouter>
  );

所以第一组花括号表示我将传入纯JavaScript变量,第二组花括号是空的JavaScript对象。这是用于重定向和错误处理。

因此,在上面的答案中,仅使用Reactjs应用程序为您提供了示例,但是如果您将Reactjs与Nodejs应用程序一起使用,则我提供了答案。

BrowserRouter可以直接查看浏览器的地址栏,以找出要呈现给字符串的组件,但是需要告知StaticRouter当前路径是什么。我们必须将其传达给StaticRouter,在这种情况下,如果有Express后端,则该路径将包含在index.js文件中。

在其中,我们将有一个带有req对象的路由处理程序,如下所示:

app.use(express.static("public"));
app.get("/", (req, res) => {
  res.send(renderer(req));
});

在该res.send()中,我从renderer()文件中传入了helper/renderer.jsreq对象包含您要尝试访问的地址的URL,因此您需要以某种方式将该req对象与StaticRouter文件中定义的helper/renderer.js进行通信,您如上所见,可以通过将req传递到renderer()来做到这一点。

然后在helper/renderer.js中,您可以将其作为req来接收,并带有location的附加属性,而真正的url将是请求对象上的属性,即req.url像这样:

export default req => {
  const content = renderToString(
    <StaticRouter location={req.url} context={{}}>
      <Routes />
    </StaticRouter>
  );

您可以在此处了解更多信息:https://expressjs.com/en/api.html#req.path