ReactJS因大型DOM的频繁更新而缓慢?

时间:2014-11-20 20:15:16

标签: javascript reactjs

我们正在考虑将React用于DOM重型项目,并想要弄清楚虚拟DOM渲染方法的性能特征。

我担心的一件事是,虚拟DOM在每次次要状态更改时都会重新计算。我可以看到这个模型的好处,但是在一个包含大量元素和频繁的小更新的应用程序中,这会导致很多开销,就像“悬停”效果一样简单。

例如,这会渲染一系列N div并更改CSS类onMouseOver。在我的系统上,N=5000http://jsfiddle.net/estolua/aopvp7zp/)附近变得非常缓慢。

var D = React.DOM;

function generateItems(N) {
  return _.map(
    _.range(0, N), function (i) { return { id: '_' + i, content: '' + i }; }
  );
}

function toggle(x, y) { return (y && x!==y) ? y : null; }

var Item = React.createClass({
  render: function () {
    var item = this.props.item,
        appS = this.props.appState,
        focF = this.props.focF;
    return D.div({
        className: 'item' + (appS.focused === item.id ? ' focused' : ''),
        onMouseOver: focF
      },
      item.content
    );
  }
});

var App = React.createClass({
  getInitialState: function () { return {N: 10, focused: null}; },
  changeN: function(e) { this.setState({N: e.target.value}); },
  focus: function (id, e) {
    this.setState({focused: toggle(this.state.focused, id)});
  },
  render: function () {
    var that = this;
    return D.div(null, [
      D.div(null, [
        D.span(null, 'N: '),
        D.input({value: this.state.N, onChange: this.changeN})
      ]),
      D.div(null,
        _.map(
          generateItems(this.state.N),
          function (i) { return React.createElement(Item, {
            key: i.id, item: i, appState: that.state, focF: that.focus.bind(null, i.id)
          });}
        )
      )
    ]);
  }
});

React.render(React.createElement(App), document.body);

有没有办法让这些小更新更有效率而不放弃漂亮的声明性表格,或者React只是不适合这种规模?

3 个答案:

答案 0 :(得分:9)

有一些潜在的解决方案可以让您继续使用漂亮的声明性模型:

1。使用shouldComponentUpdate

当你有很多元素时,shouldComponentUpdate可以轻松获胜。您评论中的示例不太正确;相反,您希望查看您关注的任何this.props值是否与您关注的任何next_props不同(this.statenext_state也是如此。例如,要仅在焦点道具发生变化时重新渲染,您可以执行以下操作:

shouldComponentUpdate: function (next_props, next_state) {
  return this.props.appState.focused !== next_props.appState.focused;
},

(虽然这个简单的例子不处理ID或处理程序更改)。

虽然这是非常手动的,但您可以轻松地构建抽象或混合,以比较对象(浅或深,取决于道具的结构),看看它们是否完全改变了:

var ShallowPropStateCompareMixin = {
  shouldComponentUpdate: function(nextProps, nextState) {
    return !shallowEquals(nextProps, this.props) ||
           !shallowEquals(nextState, this.state);
  }
}

var MyComponent = React.createClass({
  mixins: [ShallowPropStateCompareMixin],

  // ...
});

事实上,这已经实现为the PureRenderMixin。你可以在this example看到这个工作(注意其他东西可能会导致很多DOM元素的迟缓,包括影响盒子模型的扩展和内联样式)。

2。本地化状态更改

您可以使用的另一种技术是本地化状态变化;在您的示例中,顶级应用程序状态会随着每个项目悬停或取消暂停而更改;相反,您可以将此行为委托给项目本身(或项目的其他容器)。这样,只有单个容器项目会有状态更改,大多数项目根本不会重新呈现。

我还没有尝试的另一个想法是将n个项中的x组合在一个分组组件中(例如,n = 5000和x = 100);那么,只有包含已更改项的分组组件才需要更新。您可以在分组组件上使用shouldComponentUpdate,以便不需要迭代其他组件。

答案 1 :(得分:2)

在我的系统上5000仍然可以,但你真的需要渲染>真实场景中的1000个dom节点?如果您非常关心性能,可以考虑Mithril。它也使用虚拟dom方法,但实际上是小而快。

答案 2 :(得分:1)

如果将shouldComponentUpdate添加到Item组件,它会变得更快一些。因为它只呈现正在改变的项目。但是我的机器上有5000件物品似乎有点慢。

var Item = React.createClass({

    shouldComponentUpdate: function(nextProps, nextState){
        return (nextProps.appState.focused==nextProps.item.id) || (this.props.appState.focused==this.props.item.id);
    },