我们正在考虑将React用于DOM重型项目,并想要弄清楚虚拟DOM渲染方法的性能特征。
我担心的一件事是,虚拟DOM在每次次要状态更改时都会重新计算。我可以看到这个模型的好处,但是在一个包含大量元素和频繁的小更新的应用程序中,这会导致很多开销,就像“悬停”效果一样简单。
例如,这会渲染一系列N
div并更改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);
有没有办法让这些小更新更有效率而不放弃漂亮的声明性表格,或者React只是不适合这种规模?
答案 0 :(得分:9)
有一些潜在的解决方案可以让您继续使用漂亮的声明性模型:
shouldComponentUpdate
当你有很多元素时,shouldComponentUpdate
可以轻松获胜。您评论中的示例不太正确;相反,您希望查看您关注的任何this.props
值是否与您关注的任何next_props
不同(this.state
和next_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元素的迟缓,包括影响盒子模型的扩展和内联样式)。
您可以使用的另一种技术是本地化状态变化;在您的示例中,顶级应用程序状态会随着每个项目悬停或取消暂停而更改;相反,您可以将此行为委托给项目本身(或项目的其他容器)。这样,只有单个容器项目会有状态更改,大多数项目根本不会重新呈现。
我还没有尝试的另一个想法是将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);
},