我以TodoList示例来反射(reflect)我的问题,但显然我的实际代码更加复杂。

我有一些这样的伪代码。

var Todo = React.createClass({
  mixins: [PureRenderMixin],
  ............
}

var TodosContainer = React.createClass({
  mixins: [PureRenderMixin],

  renderTodo: function(todo) {
     return <Todo key={todo.id} todoData={todo} x={this.props.x} y={this.props.y} .../>;
  },

  render: function() {
     var todos = this.props.todos.map(this.renderTodo)
     return (
          <ReactCSSTransitionGroup transitionName="transition-todo">
                 {todos}
          </ReactCSSTransitionGroup>,
     );
  }

});

我所有的数据都是不可变的,并且使用了PureRenderMixin并且一切正常。修改待办事项数据后,仅父项和已编辑的待办事项会重新呈现。

问题在于,随着用户的滚动,有时我的列表会越来越大。并且,当更新单个待办事项时,渲染父项,在所有待办事项上调用shouldComponentUpdate并渲染单个待办事项需要花费越来越多的时间。

如您所见,Todo组件具有与Todo数据不同的其他组件。这是所有待办事项渲染所需的数据并被共享(例如,我们可以想象待办事项有一个“displayMode”)。具有许多属性会使shouldComponentUpdate的执行速度变慢。

另外,使用ReactCSSTransitionGroup似乎也有点慢,因为ReactCSSTransitionGroup必须甚至在调用todos的ReactCSSTransitionGroupChild之前就渲染自己和shouldComponentUpdateReact.addons.Perf显示ReactCSSTransitionGroup > ReactCSSTransitionGroupChild渲染浪费了列表的每个项目。

因此,据我所知,我使用PureRenderMixin,但列表较大时,这可能还不够。我仍然表现不差,但是想知道是否有简单的方法可以优化我的渲染。

任何的想法?

编辑:

到目前为止,我的大列表是分页的,因此,我没有大的项目列表,而是现在将此大列表拆分为页面列表。由于每个页面现在都可以实现shouldComponentUpdate,因此可以实现更好的性能。现在,当项目在页面中发生更改时,React只需调用在页面上迭代的主要render函数,而仅从单个页面调用render函数,这将减少迭代工作。

但是,我的渲染性能仍然与我拥有的页码(O(n))成线性关系。因此,如果我有成千上万的页面,则仍然是一个问题:)在我的用例中,这不太可能发生,但我仍然对更好的解决方案感兴趣。

我很确定可以通过将大列表拆分成树(如持久数据结构)以及每个节点的位置来实现O(log(n))渲染性能,其中n是项目(或页面)的数量。有权使用shouldComponentUpdate短路计算

是的,我正在考虑类似于Scala或Clojure中的Vector之类的持久数据结构:

但是我担心React,因为据我所知,渲染树的内部节点时可能必须创建中间dom节点。根据用例(and may be solved in future versions of React),这可能是一个问题

同样,当我们使用Javascript时,我想知道Immutable-JS是否支持它,并使“内部节点”可访问。另请:https://github.com/facebook/immutable-js/issues/541

编辑:与实验相关的有用链接:Can a React-Redux app really scale as well as, say Backbone? Even with reselect. On mobile

最佳答案

在我们的产品中,我们还遇到了与呈现的代码量有关的问题,并且我们开始使用可观察对象(请参见此blog)。这可能会部分解决您的问题,因为更改待办事项将不再需要重新渲染保存该列表的父组件(但添加仍然可以)。

它也可以帮助您更快地重新渲染列表,因为当props在shouldComponentUpdate上更改时,您的todoItem组件可能只是返回false。

为了在呈现概述时进一步提高性能,我认为您的树/分页想法确实不错。使用可观察的数组,每个页面可以开始侦听一定范围内的数组拼接(使用ES7 polyfill或可移动的)。这会带来一些管理,因为这些范围可能会随着时间而变化,但是应该使您进入O(log(n))

这样您会得到类似:

var TodosContainer = React.createClass({
  componentDidMount() {
     this.props.todos.observe(function(change) {
         if (change.type === 'splice' && change.index >= this.props.startRange && change.index < this.props.endRange)
             this.forceUpdate();
     });
  },

  renderTodo: function(todo) {
     return <Todo key={todo.id} todoData={todo} x={this.props.x} y={this.props.y} .../>;
  },

  render: function() {
     var todos = this.props.todos.slice(this.props.startRange, this.props.endRange).map(this.renderTodo)
     return (
          <ReactCSSTransitionGroup transitionName="transition-todo">
                 {todos}
          </ReactCSSTransitionGroup>,
     );
  }

});

大列表和 react 的中心问题似乎是您不能仅将新的DOM节点移入dom。否则,您根本不需要“页面”就可以将数据分成较小的块,并且您可以像在此jsFiddle中使用JQuery一样将一个新的Todo项拼接到dom中。如果您为每个待办事项使用ref,您仍然可以通过react来做到这一点,但是我认为这可能会绕过系统,因为这可能会破坏对帐系统?

10-08 03:40