这个文档非常愚蠢 - https://reacttraining.com/react-router/web/guides/server-rendering。这段代码不起作用!它没有说明如何将react-router v4与服务器端渲染一起使用。即使我没有找到任何好的和小的样板来看它是如何工作的。
请帮忙!
答案 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.js
。 req
对象包含您要尝试访问的地址的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