34

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

我试图调试这个问题,所以我把日志放在一个列模板上,这样我就可以看到渲染是如何工作的。

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

在此处输入图像描述

4

7 回答 7

70

不确定这是否对您的情况有所帮助,因为没有代码,但我们发现如果在设置数据源分页器之前设置了大型数据集,则 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 中。

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

于 2018-07-12T02:05:16.057 回答
12

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

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

{static: true}您可以使用@ViewChild. 这将使分页器在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函数:

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

<table mat-table [dataSource]="dataSource" [trackBy]="myTrackById">
于 2020-05-11T09:46:08.860 回答
6

从父组件传下来的大型 DataSet Observable

经过一番努力,我能够结合这篇文章的许多不同答案,包括上面的@turneye和 OP 选择的正确答案,以及在另一个类似的 SO 帖子上的答案,herehere

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

数据在 <500 毫秒内加载,但页面平均需要 15 秒才能呈现,因为最初我只是传递一个MatTableDataSource已分配数据的对象。

解决方案:

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

现在,完整的 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();
      });
 
    }
}
于 2020-10-07T01:05:44.003 回答
4

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

于 2019-04-09T01:55:23.377 回答
3

我发现paginatorand sortto 有时不起作用。

超过 2000 行对我有用的是:

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

晚餐快,从 10+ 秒到 2 秒:0

于 2019-09-02T07:57:22.000 回答
0

我对 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 时遇到了相反的问题.

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

于 2019-11-07T06:07:28.043 回答
0

尽管 [@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 对象,您将更改其内存地址以解决所有冲突。

于 2021-01-28T05:01:25.303 回答