服务器端渲染与反应,反应路由器和快递 [英] Server side rendering with react, react-router, and express
问题描述
我正在尝试为我的应用程式设置服务器端呈现,而我正在尝试使用很棒的反应路由器模块,以允许它处理非js情况(某些抓取工具,当用户由于某些原因关闭了js)时。但是,我遇到麻烦。我一直在使用伟大的回应 https://stackoverflow.com/a/28558545/3314701 作为指导排序,但我正在收到奇怪的错误。尝试使用 react.renderToString()
时,会得到一个持久的语法错误
。我设置的服务器端呈现错误,缺少一些明显的东西,还是其他什么?
I'm trying to set up server-side rendering for my react app and I'm trying to use the great react-router module to allow it to handle non-js situations (some crawlers, when a user had js turned off for some reason). However, I'm running into trouble. I've been using the great response here https://stackoverflow.com/a/28558545/3314701 as a guide of sorts, but I'm getting strange errors thrown at me. I get a persistent Syntax Error
when trying to use react.renderToString()
. Am I setting up the server-side rendering incorrectly, missing something obvious, or anything else?
我的设置:
基本的Express服务器
require('babel/register');
var app = express();
// misc. express config...
var Router = require('react-router'),
routes = require('../jsx/app').routes,
React = require('react');
app.use(function(req, res, next) {
var router = Router.create({location: req.url, routes: routes});
router.run(function(Handler, state) {
console.log(Handler);
var html = React.renderToString(<Handler/>);
return res.render('react_page', {html: html});
});
});
顶级反应< App />
组件
Top-level react <App/>
component
// Shims
require('intl');
require('es5-shim');
var React = require('react/addons'),
Router = require('react-router'),
Nav = require('./nav'),
injectTapEventPlugin = require("react-tap-event-plugin"),
window.React = React; // export for http://fb.me/react-devtools
// Intl
var ReactIntl = require('react-intl'),
IntlMixin = ReactIntl.IntlMixin;
var Route = Router.Route,
DefaultRoute = Router.DefaultRoute,
NotFoundRoute = Router.NotFoundRoute,
RouteHandler = Router.RouteHandler;
var App = React.createClass({
mixins: [IntlMixin],
getInitialState: function() {
return {
connected: false,
loaded: false,
user: true
};
},
render: function() {
return (
<div className="container-fluid">
<Nav/>
<RouteHandler/>
<Footer/>
</div>
);
}
});
var routes = (
<Route name="Home" path="/" handler={App}>
<DefaultRoute name="Welcome " handler={Welcome}/>
<Route name="Bar" path="/bar" handler={Bar}>
<Route name="foo" path="/foo" handler={Foo}></Route>
</Route>
);
Router.run(routes, Router.HistoryLocation , function(Handler) {
React.render(<Handler/>, document.getElementById('app'));
});
module.routes = routes;
输出:
flo-0,1,2 (err): <div className="progressbar-container" >
flo-0,1,2 (err): ^
flo-0,1,2 (err): SyntaxError: Unexpected token <
flo-0,1,2 (err): at exports.runInThisContext (vm.js:73:16)
flo-0,1,2 (err): at Module._compile (module.js:443:25)
flo-0,1,2 (err): at Module._extensions..js (module.js:478:10)
flo-0,1,2 (err): at Object.require.extensions.(anonymous function) [as .js] (/Users/user/Code/foobar/apps/flo/node_modules/babel/node_modules/babel-core/lib/babel/api/register/node.js:161:7)
flo-0,1,2 (err): at Module.load (module.js:355:32)
flo-0,1,2 (err): at Function.Module._load (module.js:310:12)
flo-0,1,2 (err): at Function.<anonymous> (/Users/user/.nvm/versions/node/v0.12.4/lib/node_modules/pm2/node_modules/pmx/lib/transaction.js:62:21)
flo-0,1,2 (err): at Function.cls_wrapMethod (/Users/user/Code/foobar/apps/bar/node_modules/newrelic/lib/shimmer.js:230:38)
flo-0,1,2 (err): at Function.<anonymous> (/Users/user/Code/foobar/apps/bar/node_modules/pmx/lib/transaction.js:62:21)
flo-0,1,2 (err): at Module.require (module.js:365:17)
flo-0,1,2 (err): at require (module.js:384:17)
推荐答案
所以,我最终自己解决了这个问题。我得到的错误是从未渲染的嵌套组件,这就是为什么js引擎抱怨一个随机的<
char。
So, I ended up solving this one myself. The error I was getting was from an un-rendered nested component, which is why the js engine was complaining about a random <
char.
现在到我的快捷设置。对于那些不知道如何使用服务器端渲染的人来说,这是非常简单的:Node或io.js可以用来调用React的 renderToString()
方法,然后将其发送到请求的客户端。您可能已经听说过这种方法带来的好处,但对于那些不了解的人来说:
And now to my express setup. For those who aren't aware of how react can be used with server-side rendering, it's fairly straightforward: Node or io.js can be used to call React's renderToString()
method on a component and then sending that to the requesting client. You've probably heard the benefits this approach brings already, but for those who don't know:
- 您可以获得更多的SEO友善性,即使谷歌可以在其抓取器中执行JS;这非常只是一个更安全的赌注
- 非js情况的回退。如果您的应用脚本缓慢加载,您仍然可以将实际页面呈现给客户端,而不要让其等待,而盯着空白屏幕。这也允许在浏览器上禁用JS的人大部分仍然与您的应用程序进行交互;链接仍然可以工作,表单仍然可以提交,< c。
- 您可以在客户端和服务器之间获得代码共享的额外优势。除了复杂性降低的事实之外,没有必要令人难以置信,因此,您可以获得复杂性降低的所有好处(潜在地减少耦合,更易于维护,更简单的结构,同构性和同等性) / li>
- 另一个好处是可以使用反应路由器的html5历史API,而不是使用烦人的哈希片段的东西,否则可以使用。
您甚至可以通过这种方法疯狂处理您的应用程序的占位符,同时加载或提供其他缓慢加载状态的反馈机制(在加载Facebook时)
You could even get crazy with this approach and handle things like placeholders for your app while it loads or provide other feedback mechanisms for a slow-loading state (a la Facebook while it loads).
基本方法的操作大致如下:
The basic approach operates roughly in the following manner:
- 引导,节点应用程序根据
routes.jsx实例化一个反应路由器实例
- 请求转到服务器,然后使用express
req.path
提供一个路由字符串反应路由器来处理。 - 反应路由器然后匹配提供的路由,并尝试呈现相应的组件以进行快速发送。
- 反应发送html响应,您的客户端可以绘制某些东西,而不管应用脚本的速度如何。我们服务于一个伟大的CDN,但即使最好的分布和压缩慢网络仍然会让人们暂时空白屏幕。
- 加载所需的应用脚本后,React可以使用相同的
routes.jsx
文件来接管并使用react-router
从这里出来。这里的另一个好处是,您的应用程序代码可以被缓存,并且将来的交互希望甚至不需要依赖另一个调用。
- Upon bootstrap, the node app instantiates a react-router instance based on
routes.jsx
- Request goes to the server, which then uses express'
req.path
to provide a route string for react-router to handle. - React router then matches the provided route and tries to render the corresponding component for express to send back.
- React sends down the html response and your client gets to paint something regardless of the speed of your app script. We serve ours over a great CDN, but even with the best distribution and compression slow networks would still otherwise leave people with a temporarily blank screen.
- Having loaded the needed app script, React can use the same
routes.jsx
file to take over and generate html withreact-router
from here on out. Another benefit here is that your app code can be cached and future interactions hopefully won't even have to rely on another call.
还有一点值得注意:我使用webpack捆绑我的反应代码,现在 browser.jsx
是入口点。在重构服务器端渲染之前,它以前是
app.jsx
;您可能需要重新配置您的结构以适应所呈现的位置。 :)
One more point worth noting: I use webpack to bundle my react code and now browser.jsx
is the entry point. Before refactoring for server-side rendering it was previously app.jsx
; you might need to re-configure your structure to accommodate what gets rendered where. :)
le代码:
Browser.jsx
const React = require('react');
const Router = require('react-router').Router;
const hist = require('history');
const routes = require('./routes');
const newHistory = hist.createHistory();
React.render(<Router history={newHistory}>{routes}</Router>, window.document);
App.js(express server):
//...other express configuration
const routes = require('../jsx/routes');
const React = require('react');
const {RoutingContext, match} = require('react-router');
const hist = require('history');
app.use((req, res, next) => {
const location = hist.createLocation(req.path);
match({
routes: routes,
location: location,
}, (err, redirectLocation, renderProps) => {
if (redirectLocation) {
res.redirect(301, redirectLocation.pathname + redirectLocation.search);
} else if (err) {
console.log(err);
next(err);
// res.send(500, error.message);
} else if (renderProps === null) {
res.status(404)
.send('Not found');
} else {
res.send('<!DOCTYPE html>' + React.renderToString(<RoutingContext {...renderProps}/>));
}
});
});
//...other express configuration
路线。 jsx
<Route path="/" component={App}>
<DefaultRoute component={Welcome}/>
<Route path="dashboard" component={Dashboard}/>
<Route path="login" component={Login}/>
</Route>
App.jsx
<html>
<head>
<link rel="stylesheet" href="/assets/styles/app.css"/>
</head>
<body>
<Navigation/>
<RouteHandler/>
<Footer/>
<body/>
</html>
有用的链接:
- https:// ifelse.io/2015/08/27/server-side-rendering-with-react-and-react-router/
- https://github.com/rackt/react-router
- https://ifelse.io/2015/08/27/server-side-rendering-with-react-and-react-router/
- https://github.com/rackt/react-router
这篇关于服务器端渲染与反应,反应路由器和快递的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!