1000 行中的 Angular 6 MatTable 性能

Posted

技术标签:

【中文标题】1000 行中的 Angular 6 MatTable 性能【英文标题】:Angular 6 MatTable Performance in 1000 rows 【发布时间】:2018-10-21 08:37:49 【问题描述】:

我在我的项目中使用了有角度的材质,并且我使用 Mat-Table 来渲染每个表 1000 个产品/行。 当将表格的分页(我们使用后端分页)更改为 1000 行时,性能变得非常慢,我什至无法在文本框中写入。

我尝试调试该问题,因此我将日志放在一个列模板上,以便查看渲染的工作原理。

即使我将鼠标悬停在表格标题上,我也看到它是重新渲染所有行。 有没有可能控制变化检测就像 ChangeDetectionStrategy.OnPush

【问题讨论】:

为什么要拉 1000 行?这是通过网络传输的大量数据。而且几乎无论您使用哪个框架,您都会看到这么多渲染的迟缓行为。 我们使用的是不带角度的 html 表格,效果很好,我们想批量操作 【参考方案1】:

我已经解决了这个问题,并通过将表格包装在自定义(网格)组件中并将组件的 changeDetection 控制为 ChangeDetectionStrategy.OnPush 来提高性能,当我想渲染更新时,我使用了 ChangeDetectorRef.detectChanges()

【讨论】:

你能详细说明一下吗? 我试过这样做,但是当我将鼠标悬停在标题上时,它仍然会再次呈现所有内容 与任何用例无关【参考方案2】:

尽管有 [@turneye's above] 解决方案,我仍然面临这个问题。对我来说,解决方案基本上是通过执行以下操作将数据源的重新分配延迟到最后可能的时刻:

ngAfterViewInit() 
    const ds = new MatTableDataSource<ProcessDelaysTable>()
    ds.sort = this.sort
    ds.paginator = this.paginator
    ds.paginator.pageSizeOptions = [5, 10, 50, 100, this.dataPayload.length]
    ds.data = this.dataPayload
    this.dataSource = ds

基本上发生此问题是因为您通过在检查后更改其数据来更改 this.datasource 上的深层属性。但是,如果您将 this.datasource 分配给新的 DataSource 对象,您会更改其内存地址以解决所有冲突。

【讨论】:

【参考方案3】:

从父组件传下来的大型数据集 Observable

经过一番努力,我能够将这篇文章的许多不同答案组合在一起,包括@turneye's above 和 OP 选择的正确答案,以及另一个类似的 SO 帖子here 和 here 的答案。

我有大约 1000 行需要用于页面上的父组件以及几个子组件,包括一个分页的 MatTable 组件

数据在 MatTableDataSource 对象。

解决办法:

    用数据传递一个observable,然后在视图初始化后订阅它,设置MatTableDataSource.data属性设置MatPaginatorMatSort之后。 在 Component 装饰器配置中将 changeDetection 设置为 ChangeDetectionStrategy.OnPush。 在observable body中设置MatDataSource.data属性后,用ChangeDetectorRef.detectChanges()告诉angular检测变化

现在完整的 DOM 总共在大约 1 秒内呈现,考虑到需要一次呈现的数据量,这很好。

这是一个精简的例子:

@Component(
    changeDetection: ChangeDetectionStrategy.OnPush,
    selector: 'app-dynamic-grid',
    templateUrl: './dynamic-grid.component.html',
    styleUrls: ['./dynamic-grid.component.scss'],
  )
  export class DynamicGridComponent implements OnInit, AfterViewInit 
    @Input() public dataSource$: Observable<any[]>;
  
    public matDataSource = new MatTableDataSource<any>();
  
    @ViewChild(MatPaginator,  static: true ) paginator: MatPaginator;
    @ViewChild(MatSort,  static: true ) sort: MatSort;
  
    constructor(private changeDetectorRef: ChangeDetectorRef) 
  
    ngOnInit(): void 
  
    ngAfterViewInit() 
      this.matDataSource.paginator = this.paginator;
      this.matDataSource.sort = this.sort;
  
      this.dataSource$.subscribe(x => 
        this.matDataSource.data = x;

        // The important part:
        this.changeDetectorRef.detectChanges();
      );
 
    

【讨论】:

这是使用服务器端排序和分页吗? @ManvirSingh 不,这是假设您一次获取整个数据集。我拥有的数据从来没有太大而不能保证服务器端分页 - 只是渲染问题,我已经解决了。【参考方案4】:

扩展 @Turneye 的答案。发生这种情况的原因是因为在所有行都被渲染之后才设置分页器,因为这就是 ngAfterViewInit 钩子告诉你的。

所以它首先呈现数据源中的所有行,然后它看到:“嘿,我得到一个分页器,让我删除所有行(除了分页器计数)”。这对于大型数据集和/或复杂的表格单元格模板显然非常慢。

您可以使用@ViewChild 上的static: true 选项来解决此问题。这将使分页器在 ngOnInit 生命周期钩子中可用,并且在任何行被渲染之前:

要盗取他的代码,可以改成这样,还是有快表的:

readonly dataSource: MatTableDataSource<LocationItem> = new MatTableDataSource();

@ViewChild(MatSort,  static: true ) sort: MatSort;
@ViewChild(MatPaginator,  static: true ) paginator: MatPaginator;

ngOnInit() 
  this.dataSource.data = [ GetLargeDataSet ];
  this.dataSource.sort = this.sort;
  this.dataSource.paginator = this.paginator;

请注意,如果您的 mat-paginator 在像 *ngIf 这样的结构指令中,这将不起作用


另一个性能问题可能是您必须声明 trackBy 函数:

为了提高性能,可以为表格提供 trackBy 函数,类似于 Angular 的 ngFor trackBy。这会告知表如何唯一标识行以跟踪数据如何随每次更新而变化。

<table mat-table [dataSource]="dataSource" [trackBy]="myTrackById">

【讨论】:

static: true 为我工作。我在使用AfterViewInit 时遇到了更改检测错误,但我不知道我可以将其推送到OnInit。超级有用! 这个技巧将我的 3000 行表的渲染速度从 5 秒加速到 0 秒。【参考方案5】:

我对 Turneye 的回答有疑问,给了我一个“expressionchangedafterithasbeencheckederror”,所以我从 jabu.hlong 的回答中获得了灵感。它将 5-10 秒的加载时间变成了不到一秒。

ngAfterViewInit() 
  this.dataSource.sort = this.sort;
  this.dataSource.paginator = this.paginator;

  setTimeout(() => 
    this.dataSource.data = getData(); // Or whatever your data source is
  );

附注(提到它是因为它似乎与我遇到的问题属于同一类问题):我发现在分页器之前设置排序是最佳做法,因为我遇到了相反的问题设置 matSortActive 时。

编辑:修复了括号和分号的问题。

【讨论】:

请简要说明您的解决方案。 我不知道你说的详细是什么意思?设置超时似乎有效,可能是因为它延迟了数据源的设置,直到表完全初始化之后。这可能有点 hacky,可能有更好的方法,但它解决了我的问题。【参考方案6】:

引入分页。这是解决 mat-table 中性能问题的唯一方法。

https://material.angular.io/components/table/overview#pagination

【讨论】:

【参考方案7】:

我发现 paginatorsort 有时不起作用。

2000 多行对我有用的是:

 ngAfterViewInit() 
      setTimeout(() => 
          this.getLargeDataSet.subscribe(largeDataSet => 
              this.dataSource.paginator = this.paginator;
              this.dataSource.sort = this.sort;
              this.dataSource.data = largeDataSet;
          );
      );
 

超级快,从 10+ 秒到 2 秒:0

【讨论】:

你是使用后端排序和分页还是前端? 前端@mostafacs【参考方案8】:

不确定这是否会对您的情况有所帮助,因为没有代码,但我们发现如果在设置数据源分页器之前设置了大型数据集,则 MatTable 加载速度非常慢。

例如 - 这需要几秒钟来渲染...

dataSource: MatTableDataSource<LocationItem> = new MatTableDataSource();
@ViewChild(MatSort) sort: MatSort;
@ViewChild(MatPaginator) paginator: MatPaginator;

ngOnInit() 
  this.dataSource.data = [GetLargeDataSet];


ngAfterViewInit() 
  this.dataSource.sort = this.sort;
  this.dataSource.paginator = this.paginator;

...但这很快

ngOnInit() 
  // data loaded after view init 


ngAfterViewInit() 
  this.dataSource.sort = this.sort;
  this.dataSource.paginator = this.paginator;

  /* now it's okay to set large data source... */
  this.dataSource.data = [GetLargeDataSet];

顺便说一句,我们只是在第二次访问该组件时才发现此问题,因为来自服务器的大型数据集正在被缓存,并且在第二次访问该组件时立即可用。如果您想将该代码留在 ngOnInit 函数中,另一种选择是将 .delay(100) 添加到您的 observable。

无论如何,这可能对您的情况有所帮助,也可能无济于事。

【讨论】:

我希望我能投票 100 万次!太糟糕了,在我花了 2 天时间对整个应用程序进行分析后,我决定尝试寻找答案. 我几乎决定使用另一个数据表库。谢了哥们。这个性能问题至少应该在文档中说明! 如果我设置了第一个分页器,但没有将数据分配给数据源,我的分页器未定义..请帮忙!这就是我所做的 - this.dataSource = new MatTableDataSource(this.reportData); this.dataSource.paginator = this.paginator; this.dataSource.sort = this.sort; @Priyanka 我也遇到同样的问题,你解决了吗? @Turneye:我对您的解决方案有疑问:***.com/questions/58518445/…

以上是关于1000 行中的 Angular 6 MatTable 性能的主要内容,如果未能解决你的问题,请参考以下文章

Angular-Slickgrid:并非所有过滤器都出现在标题行中

在 1000 万行中查找记录

python 此代码将从执行它的文件夹中的所有fastq的前1000行中提取标题信息。然后它需要

Angular `ng-click` 在 DataTables 表行中不起作用

安装 Angular CLI 时出错 [重复]

Angular.forEach用法总结