React.js和大型表格

5
我需要画一个大表格,不是非常大,大约有400x400个单元格。但React渲染速度太慢了,每次单击单元格时,单元格都应该被更新,这个更新需要同样巨大的时间。有什么建议可以加快速度吗?或者React只是不适合这样的任务?
这里有一个例子(表格大小稍微减小):https://jsfiddle.net/69z2wepo/15731/
var ROWSC = 400;
var COLSC = 100;

var Hello = React.createClass({
    getInitialState: function () {
        return {
            t: { "1-1": 'c' }
        };
    },
    clicked: function (k) {
        var t = this.state.t;
        t[k] = t[k] ? 0 : 'c';
        this.setState({  t: t  });
    },
    render: function() {
        var items = [];
        for (var r = 0; r < ROWSC; r++) {
            var cols = [];
            for (var c = 0; c < COLSC; c++) {
                var k  = ''+r+'-'+c;
                cols.push(<td key={c} onClick={this.clicked.bind(this,k)} className={this.state.t[k]}>&nbsp;</td>);
            }
            items.push(<tr key={r}>{cols}</tr>);
        }
        return <table>
            {items}
            </table>
        </div>;
    }
});

React.render(<Hello name="World" />, document.getElementById('container'));

1
你可以看到,这是O(n^2)。400100并不算太大,但仍然有点慢。如果你把它缩小到5050,你会立即看到差异。在这个链接中有一个有趣的速度比较,但React仍然胜过其他东西:https://www.codementor.io/reactjs/tutorial/reactjs-vs-angular-js-performance-comparison-knockout - EugenSunic
3个回答

2
默认情况下,React会重新渲染所有内容,但在性能问题存在的情况下,可以使用shouldComponentUpdate函数来确定哪些组件树部分在更新时应该排除。
在您的示例中,一次只能更新单个行,因此如果我们开始跟踪更新发生的行,我们可以确保只更新这一行。首先,我们必须引入一个新的组件,包装表格行,我们可以在其中放置我们的钩子。
var Row = React.createClass({
    shouldComponentUpdate: function(nextProps) {
        return nextProps.mustUpdate;
    },
    render: function() {
        return <tr>{this.props.children}</tr>;
    }
});

然后我们可以像这样使用它:
items.push(
   <Row key={r}
        mustUpdate={this.state.lastUpdatedRow === r}>
        {cols}
   </Row>);

此外,重新渲染所有这些单元格似乎是浪费的,因此我们可以引入另一个包装表格单元格的组件。
var Cell = React.createClass({
    shouldComponentUpdate: function(nextProps) {
        return this.props.selected !== nextProps.selected;
    },
    render: function() {
        var props = this.props;
        return (
            <td onClick={props.onClick.bind(null, props.col, props.row)} 
                className={props.selected ? 'c' : ''}>&nbsp;
            </td>
        );
    }
});

这应该会显著提高更新性能。如果对于您的特定问题仍然不够好,可能是因为React不适合您的使用情况。无论如何,这就是优化React程序的实质,将组件拆分成更小的组件,并确保只有实际更改的部分进行更新。


将一些标记提取到具有 shouldComponentUpdate 的组件中显着提高了速度,但仍然比 jQuery 实现慢。因此,React 在这里不适用。 - Dfr
1
@Dfr 好的,我明白了。为了举例说明,在React中展示如何回退到原始的DOM操作。如果组件具有某些性能特征,其中原始的DOM操作最合适,或者您需要与另一个DOM库集成,这可能非常有用。https://jsfiddle.net/treap/yoLhbzsj/ - Heap

0

当我在控制台中查看您的结果时,我注意到每个jsfiddle运行都会弹出以下内容:

您正在使用浏览器内JSX转换器。请确保为生产预编译您的JSX- http://facebook.github.io/react/docs/tooling-integration.html#jsx

我跟着链接看到文档警告说,在使用浏览器内JSX转换器时会对性能产生影响:

浏览器内JSX转换器相当大,并导致客户端产生不必要的计算,这是可以避免的。不要在生产中使用它

也许使用JSX预编译再进行另一个测试,会让您更好地了解是否适合使用React。


仅供完整性,这里是非JSX变体:https://jsfiddle.net/69z2wepo/15760/ - Dfr

0

两件事:

确保预编译JSX以避免缓慢。(在jsfiddle中,您会看到一个控制台警告“您正在使用浏览器内置的JSX转换器。请务必为生产环境预编译您的JSX。”

将其实现为单个组件并在其中设置状态,会导致组件必须重新处理整个表格(因为setState触发render,从而重建表格DOM)。您可以通过创建代表每个表格单元格的组件来避免这种情况。然后在您的clicked函数中,不要在Hello上调用setState,而是在被点击的单元格上调用setState。这样只有更改的单元格会重新渲染。

这对于初始渲染没有帮助,但它将显着加快由于点击而进行的UI更新。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接