通常,大多数应用程序都是由各种列表和表组成的。为了避免每次都重新发明轮子,我和许多人一样,经常使用Angular Material表。后来,这些表增长了,文本数据和嵌套元素都放置在单元格中。所有这些都会增长,并成为用户计算机上的良好负载。当然,没有人喜欢这个。在我的上一个家庭项目中,有一张桌子,桌子的单元格中大部分都充满了各种字段(您甚至可以说这是一个很大的形式)。渲染大约需要8秒钟(40 x 40表格)。那么如何为大型列表优化MatTable?
测试用例
为了帮助他人处理此问题,我编写了一个小型测试应用程序。相同的MatTable表,共有五列(第一列是元素的ID,其余为常规的MatFormField文本字段)。
看起来这是一个简单的表,但是即使使该表消耗100的时间也不会超过或少于2秒(哦,这种材料及其“顶级”性能)。
在这种情况下,渲染本身仅花费了(860 ms),但是页面冻结了2.2秒,用户感到不高兴。好吧,让我们尝试渲染一个包含300行的表。而鼓声,我们稍等片刻,发现总共花了将近10秒钟的脚本和渲染时间。也就是说,在用户希望显示300行这样的表的情况下,他的页面将冻结10秒。这是非常可怕和令人惊奇的(实际上不是)。
实际上,我仍在尝试绘制1000个元素所需的时间,但是我可怜的i7无法忍受它,并且页面不断掉落。稍后,我们将尝试使用已应用的解决方案进行此操作。解决问题
因此,每个人都可以使用基于浏览器的实用程序进行测量,但是没有解决此问题的方法。这个决定是通过对问题的实质进行推理而导致的。对问题的正确理解至少已经解决了一半。
我认为每个人都明白,这是发生,因为该表中的数据首先由脚本处理,然后在吐出一块。反过来,这正是我们看到具有特征性的悬挂的原因。发现问题。现在还有待解决。想到第一个解决方案。如果问题是一次处理所有数据,则需要使表分部分处理数据。但是该怎么做呢?让我们思考一下开箱即用的功能。首先想到的是“跟踪依据”功能。更改数据源时,将不重新呈现整个表,而仅重新更改它。让我们将此函数添加到表中:<mat-table [trackBy]="trackByFn" [dataSource]="commonDataSource">
是的,但是我们没有这样的条件,即表数据以某种方式发生了变化,而这并不是现在的话题。但是,如果我们编写Pipe会怎样呢?在初始化数据源时,它将破坏数据并将其分批提供给表。反过来,trackBy函数将有助于避免完整的渲染器。@Pipe({
name: 'splitSchedule'
})
export class SplitPipe implements PipeTransform {
public transform(value: any, takeBy: number = 4, throttleTime: number = 40): Observable<Array<any>> {
return Array.isArray(value)
? this.getSplittedThread(value, takeBy, throttleTime)
: of(value);
}
private getSplittedThread(data: Array<any>, takeBy: number, throttleTime: number): Observable<Array<any>> {
const repeatNumber = Math.ceil(data.length / takeBy);
return timer(0, throttleTime).pipe(
map((current) => data.slice(0, takeBy * ++current)),
take(repeatNumber)
);
}
}
这是一小段代码,将帮助您的硬件呈现如此大的表。将此管道应用于我们的数据源。
<mat-table [trackBy]="trackByFn"
[dataSource]="commonDataSource | splitSchedule | async">
现在让我们尝试进行测量。渲染100、300和1000个元素。

我们看到了什么?真正的成功不是我们所期望的:- 300个元素渲染速度提高1秒
- 在11秒内渲染了1000个,并且标签没有消失
- 和100个元素的渲染时间通常会长150毫秒
但是不要急于下结论,让我们先看看两种情况下页面的行为。
如您所见,在通常情况下,页面会冻结几秒钟,此时用户无法执行任何操作,同时将我们的管道与trackBy链接一起使用时,用户几乎可以立即进行表初始化,并且在使用应用程序时不会造成任何不适。希望本文能对某人有所帮助。测试应用程序的源代码在Stack Blitz上。