gpt4 book ai didi

javascript - 如何加速生成巨大 DOM 的 React render() 方法?

转载 作者:行者123 更新时间:2023-12-03 13:05:44 25 4
gpt4 key购买 nike

HtmlTable组件

想象一个简单的HtmlTable react 组件。它根据通过 data 传递给它的二维数组来渲染数据。 prop,它还可以通过 rowCount 限制列数和行数和colCount Prop 。此外,我们需要该组件能够处理大量数据(数万行)而无需分页。

class HtmlTable extends React.Component {
render() {
var {rowCount, colCount, data} = this.props;
var rows = this.limitData(data, rowCount);

return <table>
<tbody>{rows.map((row, i) => {
var cols = this.limitData(row, colCount);
return <tr key={i}>{cols.map((cell, i) => {
return <td key={i}>{cell}</td>
})}</tr>
})}</tbody>
</table>
}

shouldComponentUpdate() {
return false;
}

limitData(data, limit) {
return limit ? data.slice(0, limit) : data;
}
}

rowHeights Prop

现在我们想让用户更改行高并动态执行。我们添加一个rowHeights prop,它是行索引到行高度的映射:

{
1: 100,
4: 10,
21: 312
}

我们更改了 render方法添加 style支持<tr>如果为其索引指定了高度(并且我们还使用 shallowCompare 作为 shouldComponentUpdate ):

    render() {
var {rowCount, colCount, data, rowHeights} = this.props;
var rows = this.limitData(data, rowCount);

return <table>
<tbody>{rows.map((row, i) => {
var cols = this.limitData(row, colCount);
var style = rowHeights[i] ? {height: rowHeights[i] + 'px'} : void 0;
return <tr style={style} key={i}>{cols.map((cell, i) => {
return <td key={i}>{cell}</td>
})}</tr>
})}</tbody>
</table>
}

shouldComponentUpdate(nextProps, nextState) {
return shallowCompare(this, nextProps, nextState);
}

因此,如果用户传递 rowHeights值为 {1: 10} 的 prop ,我们只需要更新一行——第二行。

性能问题

但是,为了进行比较,React 必须重新运行整个渲染方法并重新创建数万个 <tr> s。对于大型数据集来说,这非常慢。

我考虑过使用shouldComponentUpdate ,但这并没有帮助——瓶颈在我们尝试更新之前就发生了 <tr> s。瓶颈发生在重新创建整个表以进行比较的过程中。

我想到的另一件事是缓存 render结果,然后splice ing 更改了行,但这似乎完全违背了使用 React 的目的。

有没有办法不重新运行“大”render函数,如果我知道只有一小部分会改变?

编辑:显然,缓存是可行的方法...例如,这里是 a discussion of a similar problem in React's Github. React Virtualized 似乎正在使用 a cell cache (尽管我可能会遗漏一些东西)。

编辑2:并非如此。存储和重用组件的“标记”仍然很慢。其中大部分来自协调 DOM,这是我应该预料到的。好吧,现在我完全迷路了。这就是我为准备“标记”所做的事情:

    componentWillMount() {
var {rowCount, colCount, data, rowHeights={}} = this.props;
var rows = this.limitData(data, rowCount);
this.content = <table>
<tbody>{rows.map((row, i) => {
var cols = this.limitData(row, colCount);
var style = rowHeights[i] ? {height: rowHeights[i] + 'px'} : void 0;
return <tr style={style} key={i}>{cols.map((cell, i) => {
return <td key={i}>{cell}</td>
})}</tr>
})}</tbody>
</table>
}

render() {
return this.content
}

最佳答案

对于这种特殊情况,我建议 react-virtualizedfixed-data-table正如其他人提到的。这两个组件都会通过延迟加载数据来限制 DOM 交互,即仅呈现表格的可见部分。

更一般地说,MobX documentation有一个 excellent page on react performance 。请检查一下。以下是要点。

1。使用许多小组件

mobx-react's @observer components will track all values they use and re-render if any of them changes. So the smaller your components are, the smaller the change they have to re-render; it means that more parts of your user interface have the possibility to render independently of each other.

observer allows components to render independently from their parent

2。专用组件中的渲染列表

This is especially true when rendering big collections. React is notoriously bad at rendering large collections as the reconciler has to evaluate the components produced by a collection on each collection change. It is therefore recommended to have components that just map over a collection and render it, and render nothing else:

3。不要使用数组索引作为键

Don't use array indexes or any value that might change in the future as key. Generate id's for your objects if needed. See also this blog.

4。最近取消引用值

When using mobx-react it is recommended to dereference values as late as possible. This is because MobX will re-render components that dereference observable values automatically. If this happens deeper in your component tree, less components have to re-render.

5。尽早绑定(bind)函数

This tip applies to React in general and libraries using PureRenderMixin especially, try to avoid creating new closures in render methods.

示例(未经测试)

import { observer } from 'mobx-react';
import { observable } from 'mobx';


const HtmlTable = observer(({
data,
}) => {
return (
<table>
<TBody rows={data} />
</table>
);
}

const TBody = observer(({
rows,
}) => {
return (
<tbody>
{rows.map((row, i) => <Row row={row} />)}
</tbody>
);
});

const Row = observer(({
row,
}) => {
return (
<tr key={row.id} style={{height: row.rowHeight + 'px'}}>
{row.cols.map((cell, i) =>
<td key={cell.id}>{cell.value}</td>
)}
</tr>
);
});

class DataModel {
@observable rows = [
{ id: 1, rowHeight: 10, cols: [{ id: 1, value: 'one-1' }, { id: 2, value: 'one-2' }] },
{ id: 2, rowHeight: 5, cols: [{ id: 1, value: 'two-1' }, { id: 2, value: 'two-2' }] },
{ id: 3, rowHeight: 10, cols: [{ id: 1, value: 'three-1' }, { id: 2, value: 'three-2' }] },
];
@observable rowLimit = 10;
@observable colLimit = 10;

@computed
get limitedRows() {
return this.limitData(this.rows, this.rowLimit).map(r => {
return { id: r.id, cols: this.limitData(r.col, this.colLimit) };
});
}

limitData(data, limit) {
return limit ? data.slice(0, limit) : data;
}
}

const data = new DataModel();

React.render(<HtmlTable data={data.limitedRows} />, document.body);

来源:

关于javascript - 如何加速生成巨大 DOM 的 React render() 方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38551077/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com