В настоящее время я делаю древовидное представление в моем приложении 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] не слушает изменения, если родительский узел НЕТ расширен. Не раскрыто = все равно не отображается на странице.
Итак, реальный вопрос: можно ли запретить элементам прислушиваться к изменениям, если выполняется условие? И если да, то поможет ли это? Поскольку это в основном представит другого слушателя, который на самом деле ничего не решит.
4) Создайте стиль в методе handleKeyboardEvent, чтобы у вас был только один вызов функции в документе.
@ritaj Просто проверяю: вы действительно удерживаете клавиши со стрелками в течение нескольких секунд, а затем отпускаете, верно? На моем ПК (i7-7700k и 32 ГБ ОЗУ) и остальных ПК в офисе он не прокручивается, как при использовании 100 элементов.
Да ладно, я установил его на 1000, и он работал, но около 3000 он начинает работать медленно.
Ладно, похоже, что когда я не создаю новый объект каждый раз, он чертовски хорошо работает. Однако в моем GetClass есть немного логики, которая в основном проверяет, есть ли у моего узла состояние и так далее (вот почему я возвращаю объект). Вот пример, где каждый узел также имеет состояние для отображения цвета: stackblitz.com/edit/angular-vqfpte
В приведенном выше примере, если бы мне пришлось повторно использовать объекты, мне бы в основном пришлось создать по одному для каждого случая (если state == failed && selected, if state == completed && selected, if state == failed, AND if state = = завершено). И у меня довольно много состояний, ха-ха
Да, это не похоже на хорошее решение. Я попытался обработать стили в handleKeyboardEvent, но он все еще медленный из-за document.querySelector. Сейчас попробую использовать ViewChildren.
@ritaj Вот пример (который, к сожалению, не работает): stackblitz.com/edit/angular-vqfpte - я создал объект для каждого из возможных состояний, и он немного быстрее, но все же очень медленно :(





Итак, у меня получилось, что вроде работает так:
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+ элементами!
Просто хотел сказать, что замена ngClass на интерполяцию шаблонов class + была для меня победителем. Я просто взял смехотворно медленный шаблон (блокируя поток пользовательского интерфейса до минуты) с временем рендеринга 15 мс :) Обнаружение изменений уже было настроено на OnPush и не сильно помогло, поэтому я был удивлен и обрадован, обнаружив, что интерполяция такая эффективно! Спасибо!!
Для меня он не медленный, поэтому я не могу его проверить. Попробуйте 1) возвращать один и тот же объект вместо того, чтобы каждый раз создавать новый. 2) Не вызывайте функцию вообще, вычисляйте материал в шаблоне. 3) Попробуйте использовать геттеры вместо обычной функции. Любой из них должен заставить его работать быстрее. Angular будет вызывать функцию в каждом цикле обнаружения изменений и каждый раз создавать экземпляры и собирать мусор для этих объектов.