每页上的React-router服务器端呈现

时间:2015-08-03 09:18:35

标签: express reactjs server-side react-router isomorphic-javascript

我正在尝试使用express和react-router构建一个同构应用程序,数据获取和首先渲染服务器端和数据操作客户端。

我设法获取初始数据服务器端并呈现jsx组件,但只有在直接命中url而不是跟随链接时它才有效。事实上,就像我读过的所有例子一样,应用程序只进行一次服务器呈现,然后一切都发生在客户端。

另外,如果我获取一些数据,渲染组件服务器端然后按照链接,则不会为新页面更新数据。

我不知道我是否想做一些毫无意义的事情?

我想获得的是:

  • 每个页面的预渲染服务器端,无论用户是直接到达还是通过链接到达
  • 只获取所需的初始数据,该路线对应的组件要求
  • [BONUS]对组件的布局应用小改动(标题,附加css等)

这是我到目前为止所做的:

快递应用:

var express = require('express');
require('node-jsx').install();
var React = require('react');  
var Router = require('react-router'); 
var routes = require('./routes');
var url  = require('url');
var resolveHash = require('when/keys').all;


var app = express();

/*
 ....
*/

app.use(function(req, res, next) {
  Router.run(routes, url.parse(req.url).pathname, function(Handler, state){
      // create the promises hash
      var promises = state.routes.filter(function (route) {
        // gather up the handlers that have a static `fetchData` method
        return route.handler.fetchData;
      }).reduce(function (promises, route) {
        // reduce to a hash of `key:promise`
        promises = route.handler.fetchData(state.params);
        return promises;
      }, {});

      resolveHash(promises).then(function (data) {
        var html = '<!DOCTYPE html>' + React.renderToString(React.createFactory(Handler)({path:url.parse(req.url).pathname, initialData:safeStringify(data)}));
        res.send(html);
      });
    });

  // A utility function to safely escape JSON for embedding in a <script> tag
    function safeStringify(obj) {
      return JSON.stringify(obj).replace(/<\/script/g, '<\\/script').replace(/<!--/g, '<\\!--')
    }

});

routes.js:

var React = require("react");
var Router = require("react-router");

var Route = Router.Route;
var DefaultRoute = Router.DefaultRoute;
var NotFoundRoute = Router.NotFoundRoute;

var Layout = require("./components/layout.jsx");
var Stuff = require("./components/stuff.jsx");
var Home = require("./components/home.jsx");

var routes = (  
  <Route path="/" handler={Layout}>
    <Route path="/stuff" handler={Stuff} />
    <DefaultRoute handler={Home}/>
  </Route>
);

module.exports = routes;

if (typeof document !== 'undefined') { 
  var initialData = JSON.parse(document.getElementById("initialData").innerHTML);
    Router.run(routes, Router.HistoryLocation, function (Handler) {
      React.render(<Handler initialData={initialData}/>, document);
    });
 }

layout.jsx:

'use strict';  
var React = require('react');  
var Router = require('react-router');  
var RouteHandler = Router.RouteHandler;  
var Link = Router.Link;

var Layout = React.createClass({  
  render: function () {
    return (
      <html>
        <head>
          <title>{this.props.initialData.title}</title>
        </head>
        <body>
          <nav>
            <Link to={'/'}>Home</Link>
            <Link to={'/stuff'}>Stuff</Link>
          </nav>
          <RouteHandler/>
          <script id='initialData' type="application/json" dangerouslySetInnerHTML={{__html:this.props.initialData}}></script>
          // actually bundle.js is just made of routes.js as I put the client side render there
          <script src="/bundle.js"></script>
        </body>
      </html>
    );
  }
});


module.exports = Layout;

home.jsx:

'use strict';  
var React = require('react');

var Home = React.createClass({  

  statics: {
    fetchData: function(params){
      return {test:[1,2,3], title:'Home'};
    }
  },

  render: function () {
    return (
      <div className="content">
        <section>
          <article>
            <h2>Title</h2>
            <p>Body</p>
          </article>
        </section>
        <aside>
          Ads
        </aside>
      </div>
    );
  }
})

module.exports = Home;

stuff.jsx:

'use strict';  
var React = require('react');

var Stuff = React.createClass({  

    statics: {
    fetchData: function(params){
      return {test:[4,5,6], title:'Stuff'};
    }
  },

  render: function(){
    return (<h1>Hello world from thingy!</h1>)
  }
})

module.exports = Stuff;

我错过了什么,误会,做错了什么?

1 个答案:

答案 0 :(得分:0)

这是它的工作方式,它将为您提供首次加载并构建该html并进行渲染,之后它无法更改生成的html,除非您通过以下方式刷新页面点击刷新按钮或使用代码window.location