首页 文章

服务器端渲染与react,react-router和express

提问于
浏览
29

我试图使用伟大的react-router模块来允许它处理非js情况(一些爬虫,当用户由于某种原因关闭js时) . 但是,我已经在这里使用了很好的回复https://stackoverflow.com/a/28558545/3314701作为各种指导,但是我发现了一些奇怪的错误 . 在尝试使用 react.renderToString() 时,我得到了持久 Syntax Error . 我是否错误地设置了服务器端渲染,缺少明显的东西或其他什么?

我的设置:

Really basic Express server

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});
  });
});

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)

1 回答

  • 39

    所以,我最终自己解决了这个问题 . 我得到的错误来自一个未渲染的嵌套组件,这就是js引擎抱怨随机 < char的原因 .

    现在到我的快速设置 . 对于那些非常简单的人:可以使用Node或io.js在组件上调用React的 renderToString() 方法,然后将其发送到请求客户端 . 你've probably heard the benefits this approach brings already, but for those who don't知道:

    • 你获得了更多的SEO友好,即使谷歌已经可以在它的爬行器中执行JS;这几乎是一个更安全的赌注

    • 非js情况的后备 . 如果您的应用脚本加载缓慢,您仍然可以将实际页面呈现给客户端,而不是让他们在盯着空白屏幕时等待 . 这也允许在浏览器上禁用JS的人在大多数情况下仍然与您的应用程序进行交互;链接仍然有效,表单仍然可以提交,&c .

    • 您可以获得客户端和服务器之间代码共享的额外好处 . 除了复杂性降低这一事实之外,没有必要令人难以置信,因此,您可以获得降低复杂性的所有好处(可能更少的耦合,更易于维护,更简单的结构,同构,等等) .

    • 另一个好处是能够使用react-router的html5历史API而不是你必须使用的恼人的哈希片段内容 .

    您甚至可以对这种方法感到疯狂,并在加载时处理应用程序的占位符或为缓慢加载状态提供其他反馈机制(在加载时使用Facebook) .

    基本方法大致按以下方式操作:

    • 在引导程序中,节点应用程序基于 routes.jsx 实例化react-router实例

    • 请求转到服务器,服务器然后使用express' req.path 为react-router提供路由字符串来处理 .

    • 然后,React路由器匹配提供的路由,并尝试呈现相应的组件以便发送回来 .

    • React发送html响应,无论应用脚本的速度如何,您的客户端都会绘制一些内容 . 我们通过一个伟大的CDN为我们提供服务,但即使拥有最好的分发和压缩速度,网络仍会留下暂时空白的屏幕 .

    • 加载了所需的应用程序脚本后,React可以使用相同的 routes.jsx 文件来接管并从此处生成带有 react-router 的html . 这里的另一个好处是您的应用程序代码可以被缓存,未来的交互有望甚至不必依赖于另一个调用 .

    还有一点值得注意:我使用webpack捆绑我的反应代码,现在 browser.jsx 是入口点 . 在重构服务器端渲染之前,它之前是 app.jsx ;您可能需要重新配置您的结构以适应在何处呈现的内容 . :)

    代码:

    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
    

    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>
    

    有用的网址:

相关问题