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

est*_*lua 4 javascript reactjs

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

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

例如,这会渲染一系列Ndiv并更改CSS类onMouseOver.在我的系统上,它变得非常缓慢N=5000(http://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);
Run Code Online (Sandbox Code Playgroud)

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

Mic*_*ley 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;
},
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

  // ...
});
Run Code Online (Sandbox Code Playgroud)

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

2.本地化状态变化

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

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