React + Flux和服务器端渲染?(同构的React + Flux)

43

在同构应用程序中,设置应用程序的初始状态的一般做法是什么?如果没有Flux,我会简单地使用类似以下的东西:

var props = { }; // initial state
var html = React.renderToString(MyComponent(props);

然后通过 express-handlebars 渲染该标记,再通过 {{{reactMarkup}} 显示。

在客户端为了设置初始状态,我会像这样做:

if (typeof window !== 'undefined') {
    var props = JSON.parse(document.getElementById('props').innerHTML);
    React.render(MyComponent(props), document.getElementById('reactMarkup'));
}

所以,实际上你在服务器和客户端都设置了状态两次,然而React会比较差异,在大多数情况下不会通过重新渲染来影响性能。


当使用Flux架构中的actions和stores时,这个原则如何运作呢?在我的组件内部我可以这样做:

getInitialState: function() {
  return AppStore.getAppState();
}

现在我该如何从服务器上设置AppStore的初始状态呢? 如果我使用未传递属性的React.renderToString,它将调用AppStore.getAppState(),但其中不会有任何内容,因为我仍然不明白如何在服务器上设置存储中的状态。

更新于2015年2月5日

我仍在寻找一种不涉及使用第三方Flux实现(如Fluxible、Fluxxor、Reflux)的简洁解决方案。

更新于2016年8月19日

使用Redux


你好,你是否找到了不需要第三方Flux实现的解决方案? - stkvtflw
你能不能在服务器上添加一个AppStore.setAppState({...})方法?由于Node.js是单线程的,并且renderToString方法是同步的,所以应该没有问题。 - Dmitry Golubets
只需在组件中添加一个静态的getStore方法和一个load方法到Store中,在渲染之前加载数据即可。您可能还想在store中添加一个createStore方法,以便在每个请求中加载新数据之前调用它。 - nitely
4个回答

14

看一下dispatchr和雅虎相关的库。

大多数Flux实现在Node.js中不起作用,因为它们使用存储的单例调度程序和操作,没有“完成”的概念,这是必需的,以便知道何时呈现HTML并响应请求。

通过使用非常纯粹的依赖注入形式(没有解析函数用于参数名称或类似内容),Yahoo的库,如fetchr和routr,绕过了Node的这种限制。

而是像services/todo.js中定义API函数:

create: function (req, resource, params, body, config, callback) {

以及在 actions/createTodo.js 中类似这样的操作:

module.exports = function (context, payload, done) {
    var todoStore = context.getStore(TodoStore);
...
context.dispatch('CREATE_TODO_START', newTodo);
...
context.service.create('todo', newTodo, {}, function (err, todo) {

最后一行间接调用了services/todo.js中的create函数。在此情况下,"间接"可以是:

  • 在服务器端:
    • 当您处于服务器上时,fetchr会填充额外参数。
    • 然后它会调用您的回调函数。
  • 在客户端:
    • fetchr客户端发起一个HTTP请求。
    • 服务器端的fetchr截取该请求。
    • 它使用正确的参数调用服务功能。
    • 它将响应发送回客户端的fetchr。
    • 客户端的fetchr处理调用您的回调函数。

这只是冰山一角。这是一组非常复杂的模块,它们共同解决一个棘手的问题并提供可用的API。在现实世界的使用场景中,等同性本质上是很复杂的。这就是为什么许多Flux实现不支持服务器端渲染的原因。

您可能还想考虑不使用Flux。对所有应用程序来说都没有意义,通常只会妨碍开发。大多数情况下,如果需要,您只需要在应用程序的一部分中使用它。编程中没有银弹!


非常感谢您提供的出色答案。我已经查看了Yahoo的todo示例,但不喜欢它引入新的东西,比如fluxible和fetchr。但正如您所说,服务器上的Flux本质上是复杂的,因此这可能是目前唯一可行的解决方案。您可能是正确的,因为我并没有构建完整的单页应用程序 - 只是React中的一小部分,也许Flux在这里并不适合。 - Sahat Yalkabov
任何时候,如果您提前知道组件需要哪些数据,服务器端代码就非常简单。您可以获取数据,将其作为props传递给组件,并将其放入<script>标记中作为json,以便在客户端加载。当您仅在站点的一小部分中使用它时,通常是可行的。 - Brigand

3
FakeRainBrigand正确指出了服务器端Flux的最大问题是单例。Flummox通过不使用单例来解决这个问题,并使您能够将整个Flux设置封装到一个可重用的类中。然后,您只需在每个请求上创建一个新实例。结合React Router等路由解决方案,您可以创建完全同构的应用程序。
即使您不想使用Flummox,其源代码也很容易理解,您可以将其用作自己编写内容的指南:

https://github.com/acdlite/flummox


1

如果你愿意使用alt.js,你可以通过alt.bootstrapalt.flushdocs)实现它。

我正在使用Node.js与React服务器端渲染以及Alt.js作为我的Flux实现。

它看起来是这样的:

var data = {}; // Get the data whatever you want and return it bootstrap ready.

// Reminder - renderToString is synchronised
var app = React.renderToString(
     AppFactory(data)
);

// In this point the react rendering was finished so we can flush the data and reset the stores

alt.flush();

在我的 app.jsx 文件中。
/**
 *
 */
componentWillMount: function () {

    // This beauty here is that componentWillMount is run on the server and the client so this is all we need to do. No need for other third-party isomorphic frameworks

    alt.bootstrap(
        JSON.stringify(this.props, null, 3)
    );

}

0
问题在于,当你搜索“Flux服务器渲染”时,你会立即遇到这个问题,并且没有提到由React.js社区制作的Reduxrackt。你可以在Redux的文档中找到详细描述为什么服务器渲染很重要,为什么我们需要将初始状态发送到客户端的HTML中(这是Flux变得不足的地方),以及如何实现。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接