服务器端渲染与反应,反应路由器,并表示

我试图为我的反应应用程序设置服务器端渲染,我试图使用伟大的react-router模块来允许它处理非js的情况(一些爬虫,当用户closuresjs原因)。 但是,我遇到了麻烦。 我一直在使用伟大的响应在这里https://stackoverflow.com/a/28558545/3314701作为指导,但我得到了奇怪的错误抛给我。 尝试使用react.renderToString()时,我得到一个持久的Syntax Error 。 我是否设置了错误的服务器端渲染,丢失了明显的东西,还是其他的东西?

我的设置:

真正基本的快递服务器

 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/>组件

 // 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。

现在到我的快速设置。 对于那些不知道如何使用服务器端渲染的反应的人来说,这很简单:Node或io.js可以用来调用组件的React的renderToString()方法,然后发送给请求客户端。 您可能已经听到了这种方法带来的好处,但是对于那些不知道的人:

  1. 你会得到更多的search引擎友好,即使谷歌已经可以在它的抓取工具中执行JS; 这几乎是一个更安全的赌注
  2. 非js情况下的后备。 如果您的应用程序脚本加载速度较慢,则仍然可以将实际页面呈现给客户端,而不要让他们在空白屏幕上等待。 这也允许在浏览器上禁用JS的用户大部分仍然与应用程序交互; 链接仍然有效,表单仍然可以提交,&c。
  3. 您可以获得客户端和服务器之间代码共享的额外好处。 除了复杂性下降之外,没有什么必然会令人难以置信,因此,您将获得降低复杂性的所有好处(可能耦合度更低,可维护性更容易,结构更简单,同构性更强)。
  4. 另一个好处是可以使用react-router的html5历史API,而不必使用恼人的散列片段。

你甚至可以用这种方法疯狂,并为它加载或提供缓慢加载状态(加载时的Facebook)的其他反馈机制处理应用程序占位符的东西。

基本的做法大致以下列方式进行:

  1. 在引导之后,节点app基于routes.jsx实例化一个react-router实例
  2. 请求发送到服务器,然后服务器使用express的req.path为反应路由器提供一个路由string来处理。
  3. React路由器然后匹配提供的路由,并尝试渲染相应的组件快递发回。
  4. React发送HTML响应,客户端可以绘制一些东西,不pipe应用脚本的速度如何。 我们通过一个伟大的CDN为我们提供服务,但是即使是最好的分配和压缩,慢速networking也会使人暂时屏蔽掉。
  5. 在加载了所需的应用程序脚本后,React可以使用相同的routes.jsx文件来接pipe并从此处生成带有react-router html。 这里的另一个好处是可以caching你的应用程序代码,并且希望以后的交互甚至不必依靠另一个呼叫。

还有一点值得注意:我使用webpack捆绑我的反应代码,现在browser.jsx是入口点。 在重构服务器端渲染之前,它先前是app.jsx ; 你可能需要重新configuration你的结构,以适应什么地方呈现。 🙂

乐代码:

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(快递服务器)

 //...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 

Routes.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> 

有用的url: