Угловой 6 MatTable Performance в 1000 рядов.

Я использую угловой материал в своем проекте, и я использую Mat-Table для рендеринга 1000 продуктов в строке на таблицу. При изменении разбивки на страницы (мы используем внутреннюю разбивку на страницы) таблицы до 1000 строк производительность становится очень низкой, я даже не могу писать в текстовые поля.

Я попытался отладить проблему, поэтому я поместил журналы в один шаблон столбца, чтобы увидеть, как работает рендеринг.

Я вижу, что это Rerender все строки, даже если я наводил курсор на заголовки таблицы. Есть ли какие-либо возможности контролировать обнаружение изменений, как ChangeDetectionStrategy.OnPush

введите описание изображения здесь


person mostafa cs    schedule 11.05.2018    source источник
comment
Почему вы тянете 1000 строк? По сети поступает много данных. И почти независимо от того, какой фреймворк вы используете, вы увидите вялое поведение при таком большом количестве рендеринга.   -  person VtoCorleone    schedule 11.05.2018
comment
Мы использовали таблицы html без angular, и они отлично работали, и мы хотим выполнять операции навалом.   -  person mostafa cs    schedule 11.05.2018


Ответы (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];
}

Между прочим, мы обнаружили эту проблему только при втором доступе к компоненту, поскольку большой набор данных с сервера кэшировался и был немедленно доступен при втором доступе к компоненту. Другой вариант - добавить .delay (100) к наблюдаемому объекту, если вы хотите оставить этот код в функции ngOnInit.

В любом случае, это может помочь, а может и не помочь вашей ситуации.

person Turneye    schedule 12.07.2018
comment
Хотел бы я проголосовать за это 1 миллион раз! Жаль, что я решил попытаться найти ответ после того, как потратил 2 дня на профилирование всего моего приложения, думая, что у меня есть код, который вызывается много раз, и снижает производительность таблицы, потому что даже со 100 элементами в источнике данных он очень медленно обновлялся. . - person rbasniak; 04.12.2018
comment
Я почти решил пойти с другой библиотекой данных. Спасибо друг. Эта проблема производительности должна быть как минимум указана в документации! - person Hristo Enev; 16.12.2018
comment
Без присвоения данных источнику данных, если я установил первый пагинатор, я получаю пагинатор undefined .. пожалуйста, помогите! Вот как я это сделал - this.dataSource = new MatTableDataSource (this.reportData); this.dataSource.paginator = this.paginator; this.dataSource.sort = this.sort; - person Priyanka; 04.06.2019
comment
@ Приянка У меня тоже самое, ты когда-нибудь решал? - person americanslon; 07.08.2019
comment
@Turneye: у меня проблема с вашим решением здесь: stackoverflow.com/questions/58518445/ - person Michael Hunziker; 23.10.2019
comment
вау, это резко улучшило производительность моей сортировки. Спасибо - person kjohnsonthecoder; 21.12.2019
comment
Мне помогло установить данные таблицы в ngAfterViewChecked - person Dmytro Krekota; 17.09.2020
comment
Благодаря вашему решению мне удалось решить проблемы с производительностью, которые возникали даже при загрузке пакетов по 300 данных. Большое спасибо! - person WitnessTruth; 09.11.2020
comment
Я вообще не использую разбиение на страницы, как в примере Базовое использование <table mat-table> в документации, но у меня все еще очень медленное время загрузки, 10 секунд для 300 элементов - person DevEng; 04.02.2021

Я решил эту проблему и улучшил производительность, заключив таблицу в пользовательский (сеточный) компонент и установив changeDetection компонента на ChangeDetectionStrategy.OnPush, а когда я хочу визуализировать обновление, я использовал ChangeDetectorRef.detectChanges()

person mostafa cs    schedule 09.04.2019
comment
Не могли бы вы подробнее рассказать об этом? - person Jm3s; 12.05.2020

Чтобы продолжить ответ @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:

Для повышения производительности в таблице может быть предоставлена ​​функция trackBy, аналогичная функции Angular ngFor trackBy. Это сообщает таблице, как однозначно идентифицировать строки, чтобы отслеживать, как данные изменяются при каждом обновлении.

<table mat-table [dataSource]="dataSource" [trackBy]="myTrackById">
person Poul Kruijt    schedule 11.05.2020
comment
{ static: true } работал у меня. Я получал ошибки обнаружения изменений при использовании AfterViewInit, но не знал, что могу подтолкнуть их к OnInit. Очень полезно! - person mtpultz; 27.03.2021

Я обнаружил, что paginator и sort иногда не работают.

Что сработало для меня с более чем 2000 строками, было:

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

Ужин быстрый, от 10 + сек до 2 сек: 0

person jabu.hlong    schedule 02.09.2019
comment
Вы используете внутреннюю сортировку и разбиение на страницы или интерфейс? - person mostafa cs; 03.09.2019
comment
Фронтенд @mostafacs - person jabu.hlong; 03.09.2019

Наблюдаемый большой набор данных передается из родительского компонента

После долгих усилий я смог объединить кусочки множества разных ответов из этого сообщения, включая @ turneye's выше и выбранный OP правильный ответ, а также ответы на другой похожий пост SO, здесь и здесь.

У меня было ~ 1000 строк, которые мне понадобились для родительского компонента на странице, а также несколько дочерних компонентов, включая компонент MatTable с разбивкой на страницы.

Данные загружались за <500 мс, но тогда на рендеринг страницы в среднем уходит 15 секунд, поскольку изначально я просто передавал MatTableDataSource объект с уже назначенными данными.

Решение:

  1. Передайте наблюдаемый объект с данными, а затем подпишитесь на него после инициализации представления, установив свойство MatTableDataSource.data после установки MatPaginator и MatSort.
  2. Установите changeDetection в ChangeDetectionStrategy.OnPush в конфигурации декоратора Component.
  3. После установки свойства 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();
      });
 
    }
}
person KMJungersen    schedule 07.10.2020

У меня возникла проблема с ответом Турни, который дал мне «выражение изменилось после того, как оно было проверено», поэтому я черпал вдохновение из ответа 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 .

РЕДАКТИРОВАТЬ: исправлены проблемы со скобками и точками с запятой.

person atlowell    schedule 07.11.2019
comment
пожалуйста, вкратце опишите свое решение. - person Ratan Uday Kumar; 07.11.2019
comment
Я не уверен, что вы имеете в виду, говоря о подробностях? Установка тайм-аута, похоже, сработала, вероятно, потому, что это задержало настройку источника данных до тех пор, пока таблица не будет полностью инициализирована. Это, вероятно, немного взломано, и может быть лучший способ сделать это, но это устранило мою проблему. - person atlowell; 07.11.2019

Я все еще сталкивался с проблемой, несмотря на решение [@ turneye выше]. Для меня решение заключалось в том, чтобы существенно отложить переназначение источника данных до последнего возможного момента, выполнив следующие действия:

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, вы изменяете его адрес в памяти, разрешая все конфликты.

person Boolean Autocrat    schedule 28.01.2021

Ввести разбиение на страницы. Это единственный способ решить проблему производительности в мат-таблице.

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

person Ε Г И І И О    schedule 01.10.2019
comment
У него уже есть разбивка на страницы в его таблице - person Kevin Oswaldo; 08.04.2021