Acelere a renderização do MatTable em poucas etapas

Muitas vezes acontece que a maior parte do aplicativo é composta de várias listas e tabelas. Para não reinventar a roda toda vez, eu, como muitos, costumava usar as mesas de material angular .

Mais tarde, essas tabelas cresceram, dados de texto e elementos aninhados foram colocados nas células. Tudo isso cresce e se torna uma boa carga na máquina do usuário. E, claro, ninguém gosta disso.

No meu último projeto de casa, havia uma tabela cujas células eram preenchidas principalmente com vários campos (você poderia até dizer que era um formulário grande).

E a renderização levou cerca de 8 segundos (tabela 40 x 40).

Então, como você pode otimizar o MatTable para grandes listas?

imagem

Caso de teste


Para ajudar outras pessoas a lidar com esse problema, escrevi um pequeno aplicativo de teste. A mesma tabela MatTable, com cinco colunas (a primeira é a identificação do elemento, o restante são campos de texto MatFormField regulares).



Parece que esta é uma tabela simples, mas mesmo renderizando 100 o dreno dessa tabela não leva nem mais nem menos que 2 segundos (Oh, este material e seu desempenho "superior").



A renderização em si nesse caso levou apenas (860 ms), no entanto, a página congelou por 2,2 segundos e o usuário ficou chateado.

Bem, vamos tentar renderizar uma tabela com 300 linhas. E na bateria, esperamos um pouco e vemos que quase 10 segundos foram gastos em scripts e renderização no total. Ou seja, no caso em que o usuário deseja exibir essa tabela em 300 linhas, sua página será congelada por 10 segundos. Isso é muito assustador e surpreendente (na verdade não).



Na verdade, eu ainda estava tentando medir o tempo que levaria para desenhar 1000 elementos, mas meu patético i7 não aguentava e a página estava caindo constantemente.
Vamos tentar fazer isso mais tarde com a solução já aplicada.

Solução para o problema


E assim, todos podem fazer medições usando utilitários baseados no navegador, mas não há solução para esse problema.

Essa decisão me levou a raciocinar sobre a própria essência do problema.
uma compreensão correta do problema já é pelo menos metade de sua solução.
Acho que todo mundo entende que isso está acontecendo porque os dados da tabela são primeiro processados ​​por scripts e depois cuspidos em uma única peça . Por sua vez, é precisamente por isso que vemos uma suspensão característica.

Problema encontrado. Agora resta descobrir como resolvê-lo. A primeira solução vem à mente. Se o problema estiver processando todos os dados de uma só vez, você precisará fazer com que a tabela processe os dados em partes.

Mas como fazer isso?

Vamos pensar o que temos para isso fora da caixa. A primeira coisa que vem à mente é a função Rastrear por. Ao alterar a fonte de dados, a tabela inteira não será renderizada novamente, mas apenas suas alterações.

Vamos adicionar esta função à nossa tabela:

<mat-table [trackBy]="trackByFn" [dataSource]="commonDataSource">

Bem, mas não temos essa condição de que os dados da tabela estejam mudando de alguma forma, e não é sobre isso que a conversa está sendo discutida agora. Mas e se escrevermos o Pipe, que, ao inicializar a fonte de dados, irá quebrar os dados e entregá-los à tabela em lotes. Por sua vez, a função trackBy ajudará a evitar um renderizador completo.

@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)
    );
  }
}

Aqui está um pedaço de código tão pequeno que ajudará seu hardware a renderizar tabelas tão grandes.

Aplique esse canal à nossa fonte de dados.


<mat-table [trackBy]="trackByFn"
           [dataSource]="commonDataSource | splitSchedule | async">

Agora vamos tentar fazer medições. Renderize 100, 300 e 1000 elementos.







E o que vemos? O que realmente sucesso não é o que esperávamos:

  • 300 elementos renderizados 1 segundo mais rápido
  • 1000 renderizados em 11 segundos e a guia não morreu
  • e 100 elementos geralmente renderizados 150 ms a mais

Mas não se apresse em tirar conclusões, vamos primeiro examinar o comportamento da página nos dois casos.





Como você pode ver, no caso usual, a página congela por alguns segundos e o usuário não pode fazer nada no momento, enquanto usa o nosso pipe com um link para trackBy fornece ao usuário a inicialização quase instantânea da tabela e não causa nenhum desconforto ao usar o aplicativo.

Espero que este artigo ajude alguém.

O código fonte do aplicativo de teste está no Stack Blitz .

All Articles