当前位置: 首页 > 知识库问答 >
问题:

异步初始化react.js组件的服务器端呈现策略

司马庆
2023-03-14

React.js的最大优势之一应该是服务器端呈现。问题是关键函数react.renderComponentToString()是同步的,这使得在服务器上呈现组件层次结构时无法加载任何异步数据。

假设我有一个通用的评论组件,我可以把它放在页面的任何地方。它只有一个属性,某种标识符(例如,注释放在下面的文章的id),其他的事情都由组件自己处理(加载、添加、管理注释)。

我真的很喜欢Flux架构,因为它使很多事情变得容易得多,而且它的存储非常适合在服务器和客户端之间共享状态。一旦我的包含注释的存储被初始化,我就可以序列化它并将它从服务器发送到客户端,在那里它很容易被恢复。

问题是什么是最好的方式来填充我的商店。在过去的几天里,我在谷歌上搜索了很多,我遇到了很少的策略,考虑到React的这个特性被“推广”了多少,这些策略似乎都不是很好。

>

  • 在我看来,最简单的方法是在实际呈现开始之前填充我的所有存储。这意味着组件层次结构之外的某个地方(例如,与我的路由器挂钩)。这种方法的问题是,我几乎必须定义两次页面结构。考虑一个更复杂的页面,例如一个包含许多不同组件的博客页面(实际的博客文章、评论、相关文章、最新文章、twitter流……)。我必须使用React组件设计页面结构,然后在其他地方定义为当前页面填充每个所需存储的过程。我觉得这不是个好办法。不幸的是,大多数同构教程都是这样设计的(例如,这个很棒的Flux教程)。

    反应-异步。这种方法是完美的。它允许我在每个组件的一个特殊函数中定义如何初始化状态(不管是同步还是异步),并且在将层次结构呈现到HTML时调用这些函数。它的工作方式是在状态完全初始化后才呈现组件。问题是它需要纤维,据我所知,纤维是一个node.js扩展,它改变了标准JavaScript行为。虽然我真的很喜欢这个结果,但在我看来,我们并没有找到解决办法,而是改变了游戏规则。我认为我们不应该被迫这样做来使用react.js的核心特性。我也不确定这种解决方案的普遍支持度。是否可以在标准Node.js web托管上使用光纤?

    我一个人在想。我还没有考虑过具体的实现细节,但一般的想法是,我将以类似的方式扩展组件,使其成为React-async,然后在根组件上重复调用React.RenderComponentToString()。在每个传递过程中,我将收集扩展回调,然后在传递的and处调用它们来填充存储。我将重复此步骤,直到填充当前组件层次结构所需的所有存储。有很多事情要解决,我特别不确定的表现。

    我错过什么了吗?是否有其他方法/解决方案?现在,我正在考虑使用React-Async/Fibers方式,但我不完全确定,正如第二点所解释的那样。

    关于GitHub的相关讨论。显然,没有官方的办法,甚至没有解决办法。也许真正的问题是React组件打算如何使用。像简单的视图层(几乎是我的第一个建议)还是像真正独立的组件?

  • 共有2个答案

    钱志
    2023-03-14

    我知道这可能不完全是您想要的,也可能没有意义,但我记得稍微修改一下组件以处理这两种情况:

    • 在服务器端呈现,所有初始状态都已检索,必要时异步)
    • 在客户端呈现,需要时使用ajax

    比如:

    /** @jsx React.DOM */
    
    var UserGist = React.createClass({
      getInitialState: function() {
    
        if (this.props.serverSide) {
           return this.props.initialState;
        } else {
          return {
            username: '',
            lastGistUrl: ''
          };
        }
    
      },
    
      componentDidMount: function() {
        if (!this.props.serverSide) {
    
         $.get(this.props.source, function(result) {
          var lastGist = result[0];
          if (this.isMounted()) {
            this.setState({
              username: lastGist.owner.login,
              lastGistUrl: lastGist.html_url
            });
          }
        }.bind(this));
    
        }
    
      },
    
      render: function() {
        return (
          <div>
            {this.state.username}'s last gist is
            <a href={this.state.lastGistUrl}>here</a>.
          </div>
        );
      }
    });
    
    // On the client side
    React.renderComponent(
      <UserGist source="https://api.github.com/users/octocat/gists" />,
      mountNode
    );
    
    // On the server side
    getTheInitialState().then(function (initialState) {
    
        var renderingOptions = {
            initialState : initialState;
            serverSide : true;
        };
        var str = Xxx.renderComponentAsString( ... renderingOptions ...)  
    
    });
    

    很抱歉,我手边没有确切的代码,所以这可能不是开箱即用的,但我是为了讨论的兴趣才发帖的。

    同样,其想法是将组件的大部分视为哑视图,并处理从组件中尽可能多地提取数据。

    朱高丽
    2023-03-14

    如果使用react-router,只需在组件中定义一个WillTransitionTo方法,该方法会传递一个Transition对象,您可以在该对象上调用.Wait

    renderToString是否同步并不重要,因为在所有.waitED承诺都得到解决之前,不会调用对router.run的回调,因此在中间件中调用renderToString时,您已经填充了存储区。即使存储是单例,您也可以在同步呈现调用之前临时设置它们的数据,组件将看到这些数据。

    中间件示例:

    var Router = require('react-router');
    var React = require("react");
    var url = require("fast-url-parser");
    
    module.exports = function(routes) {
        return function(req, res, next) {
            var path = url.parse(req.url).pathname;
            if (/^\/?api/i.test(path)) {
                return next();
            }
            Router.run(routes, path, function(Handler, state) {
                var markup = React.renderToString(<Handler routerState={state} />);
                var locals = {markup: markup};
                res.render("layouts/main", locals);
            });
        };
    };
    

    code>路由对象(描述路由层次结构)与客户端和服务器逐字共享

     类似资料:
    • 我在尝试用异步数据初始化过滤器时遇到了麻烦。 过滤器非常简单,它需要将路径转换为名称,但要做到这一点,它需要一个对应数组,我需要从服务器获取该数组。 在返回函数之前,我可以在过滤器定义中做一些事情,但是异步方面阻止了这一点 使用promise可能是可行的,但我不清楚角度负载是如何过滤的。这篇文章解释了如何通过服务实现这样的魔力,但是对于过滤器也可以这样做吗? 如果有人对如何翻译这些路径有更好的想法

    • 我有一个AngularJS服务,我想用一些异步数据初始化它。类似这样的事情: 显然,这是行不通的,因为如果在返回之前,有东西试图调用,我将得到一个空指针异常。据我所知,从这里和这里所问的其他一些问题中,我有几个选择,但没有一个看起来很清楚(也许我遗漏了什么): 使用“运行”设置服务 设置应用程序时,请执行以下操作: 那么我的服务将如下所示: 这在某些时候起作用,但如果异步数据的时间恰好比初始化所有

    • 问题内容: 我有一个AngularJS服务,我想使用一些异步数据进行初始化。像这样: 显然,这是行不通的,因为如果在返回之前尝试进行调用,我将得到一个空指针异常。据我从阅读这里和这里提出的其他一些问题中所知道的,我有一些选择,但是它们似乎都不是很干净(也许我错过了一些东西): 安装服务“运行” 设置我的应用程序时,请执行以下操作: 然后我的服务将如下所示: 这有时会起作用,但是如果异步数据恰好比初

    • 问题内容: 我认为我在概念上使用React.js在服务器端渲染中缺少一些东西 假设我想创建一个页面来显示服务器端数据库中的项目,并使用输入字段对其进行过滤。 我想要一个页面: 响应类似的URL 带有React输入字段以按名称过滤项目 与React组件一起显示已过滤项目的列表 假设我有一个公共的REST API来查询客户端上的项目。 从概念上讲,我首先要执行的操作()是: 我希望输入字段显示用户作为

    • 问题内容: 我在尝试使用异步数据初始化过滤器时遇到麻烦。 过滤器非常简单,它需要将路径转换为名称,但是这样做需要一个对应数组,我需要从服务器中获取该数组。 在返回函数之前,我可以在过滤器定义中执行操作,但是异步方面阻止了该操作 使用诺言可能是可行的,但我对角负载如何过滤没有明确的了解。这篇文章解释了如何通过服务实现这种魔力,但是是否有可能对过滤器做同样的事情? 而且,如果有人对如何翻译这些路径有更

    • SOFABoot 提供了模块并行启动以及 Spring Bean 异步初始化能力,用于加快应用启动速度。本文介绍如何使用 SOFABoot 异步初始化 Spring Bean 能力以提高应用启动速度。 使用场景 在实际使用 Spring/Spring Boot 开发中,一些 Bean 在初始化过程中执行准备操作,如拉取远程配置、初始化数据源等等。在应用启动期间,这些 Bean 会增加 Spring