我正在尝试使用具有数据获取功能的 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, '<\\!--')
}
});
路线.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);
});
}
布局.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;
主页.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;
东西.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;
我错过了什么,误解,做错了什么?