NgClass на многих элементах делает сайт очень медленным

В настоящее время я делаю древовидное представление в моем приложении Angular 6, которое у меня работает (вложенное и все такое). Одна из проблем, с которыми я сталкиваюсь, заключается в том, что когда на моей странице много элементов (несколько тысяч), и все они содержат [ngClass] (чтобы отображать разные цвета в зависимости от выбранного узла), страница имеет тенденцию сильно зависать. Я создал StackBlitz, чтобы показать свою проблему: https://stackblitz.com/edit/angular-atveai

Чтобы проверить это, просто удерживайте клавиши со стрелками вверх / вниз на правом экране вывода. Это должно быть очень медленно. Если вы установите в цикле отображение только 100 элементов вместо 10000, он будет работать безупречно (из-за меньшего количества элементов).

Чтобы поймать событие keydown (которое я хочу в своем документе), я делаю следующее:

@Component({
  host: {
    '(document:keydown)': 'handleKeyboardEvent($event)'
  }
})

Это вызывает handleKeyboardEvent() с объектом $event.

В файле HTML у меня есть очень простой *ngFor, где каждый элемент имеет [ngClass] = "GetClass(item)". По сути, это возвращает объект, содержащий все классы, которые должны быть применены. В моем случае, если выбранный узел равен элементу, он устанавливает obj["selected"] = true, так что один элемент получит класс selected.

Я полагаю, это довольно сложно, потому что каждый элемент вызывает этот метод много раз. Я могу предположить, почему это происходит медленно.

Моя страница может легко иметь от 5000 до 10000 узлов (то, что мы действительно не хотим менять, если есть способ исправить это). Однако количество корневых узлов, вероятно, составляет от 10 до 30. Многие из узлов вложены как дочерние (в основном 99% из них вложены).

Я думал, что [ngClass] не слушает изменения, если родительский узел НЕТ расширен. Не раскрыто = все равно не отображается на странице.

Итак, реальный вопрос: можно ли запретить элементам прислушиваться к изменениям, если выполняется условие? И если да, то поможет ли это? Поскольку это в основном представит другого слушателя, который на самом деле ничего не решит.

Для меня он не медленный, поэтому я не могу его проверить. Попробуйте 1) возвращать один и тот же объект вместо того, чтобы каждый раз создавать новый. 2) Не вызывайте функцию вообще, вычисляйте материал в шаблоне. 3) Попробуйте использовать геттеры вместо обычной функции. Любой из них должен заставить его работать быстрее. Angular будет вызывать функцию в каждом цикле обнаружения изменений и каждый раз создавать экземпляры и собирать мусор для этих объектов.

Roberto Zvjerković 18.10.2018 14:42

4) Создайте стиль в методе handleKeyboardEvent, чтобы у вас был только один вызов функции в документе.

Roberto Zvjerković 18.10.2018 14:47

@ritaj Просто проверяю: вы действительно удерживаете клавиши со стрелками в течение нескольких секунд, а затем отпускаете, верно? На моем ПК (i7-7700k и 32 ГБ ОЗУ) и остальных ПК в офисе он не прокручивается, как при использовании 100 элементов.

MortenMoulder 18.10.2018 15:01

Да ладно, я установил его на 1000, и он работал, но около 3000 он начинает работать медленно.

Roberto Zvjerković 18.10.2018 15:07

Ладно, похоже, что когда я не создаю новый объект каждый раз, он чертовски хорошо работает. Однако в моем GetClass есть немного логики, которая в основном проверяет, есть ли у моего узла состояние и так далее (вот почему я возвращаю объект). Вот пример, где каждый узел также имеет состояние для отображения цвета: stackblitz.com/edit/angular-vqfpte

MortenMoulder 18.10.2018 15:11

В приведенном выше примере, если бы мне пришлось повторно использовать объекты, мне бы в основном пришлось создать по одному для каждого случая (если state == failed && selected, if state == completed && selected, if state == failed, AND if state = = завершено). И у меня довольно много состояний, ха-ха

MortenMoulder 18.10.2018 15:13

Да, это не похоже на хорошее решение. Я попытался обработать стили в handleKeyboardEvent, но он все еще медленный из-за document.querySelector. Сейчас попробую использовать ViewChildren.

Roberto Zvjerković 18.10.2018 15:27

@ritaj Вот пример (который, к сожалению, не работает): stackblitz.com/edit/angular-vqfpte - я создал объект для каждого из возможных состояний, и он немного быстрее, но все же очень медленно :(

MortenMoulder 18.10.2018 15:35
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Angular и React для вашего проекта веб-разработки?
Angular и React для вашего проекта веб-разработки?
Когда дело доходит до веб-разработки, выбор правильного front-end фреймворка имеет решающее значение. Angular и React - два самых популярных...
Эпизод 23/17: Twitter Space о будущем Angular, Tiny Conf
Эпизод 23/17: Twitter Space о будущем Angular, Tiny Conf
Мы провели Twitter Space, обсудив несколько проблем, связанных с последними дополнениями в Angular. Также прошла Angular Tiny Conf с 25 докладами.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
Мое недавнее углубление в Angular
Мое недавнее углубление в Angular
Недавно я провел некоторое время, изучая фреймворк Angular, и я хотел поделиться своим опытом со всеми вами. Как человек, который любит глубоко...
Освоение Observables и Subjects в Rxjs:
Освоение Observables и Subjects в Rxjs:
Давайте начнем с основ и постепенно перейдем к более продвинутым концепциям в RxJS в Angular
4
8
1 394
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Итак, у меня получилось, что вроде работает так:

1) Используйте обнаружение изменений OnPush

@Component({
      selector: 'my-app',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.css'],
      host: {
        '(document:keydown)': 'handleKeyboardEvent($event)'
      },
      changeDetection: ChangeDetectionStrategy.OnPush
    })

2) Используйте интерполяцию шаблона вместо вызовов функций

<span class = "{{item.state === 'failed' ? 'failed' : 'completed'}} {{item.ordering === selected ? 'selected' : ''}}">{{item.name}}</span>

Он работает быстро с 5000 строками, но все еще медленно с 10000. К сожалению, лучшее, что я мог сделать.

https://stackblitz.com/edit/angular-1sg1mh?file=src%2Fapp%2Fapp.component.ts

Использование ChangeDetectionStragety.OnPush определенно значительно повысило производительность! И простой синтаксический анализ классов на основе состояний также намного быстрее, чем [ngClass]. Большое спасибо - теперь он отлично работает с 5000+ элементами!

MortenMoulder 18.10.2018 16:09

Просто хотел сказать, что замена ngClass на интерполяцию шаблонов class + была для меня победителем. Я просто взял смехотворно медленный шаблон (блокируя поток пользовательского интерфейса до минуты) с временем рендеринга 15 мс :) Обнаружение изменений уже было настроено на OnPush и не сильно помогло, поэтому я был удивлен и обрадован, обнаружив, что интерполяция такая эффективно! Спасибо!!

chrisfrancis27 01.09.2020 22:22

Другие вопросы по теме