Или как предположения могут испортить производительность вашего приложения.

У нас есть довольно большое приложение Angular, использующее PixiJS для большого количества рендеринга. Из-за анимации и постоянного обновления области холста нам пришлось как можно больше оптимизировать, чтобы приложение работало на планшетах достаточно хорошо.

Применены все стандартные оптимизации.

Почти для всех компонентов установлено значение ChangeDetectionStrategy.onPush, асинхронные и широковещательные передачи используют runOutsideAngular, ведение журнала в рабочей среде подавляется и т. Д.

Мы использовали кеш текстур PixiJS и не выполняем никаких .update() вызовов каких-либо экранных объектов, а render() вызов requestAnimationFrame выполняется вне angular.

Один из упущений - это размер определенных стеков вызовов, когда происходят важные события, связанные с изменением данных, но в остальном все кажется довольно ограниченным.

И все же у нас все еще были пользователи, замечавшие проблемы с производительностью.

Много идей для этого. Используйте веб-воркеров для транспорта, распределяйте функциональные блоки по отдельным стекам вызовов, чтобы уменьшить потерю кадров, заставляйте абсолютно все компоненты использовать ChangeDetectionStrategy.onPush, отсоединяйте компоненты и повторно подключайтесь для обновлений…

Был проведен ряд исследований проблем с производительностью Angular и PixiJS.

Затем, исследуя затраты памяти и времени для привязки angular, субъектов RxJS и объектов привязки Angular, я создал быстрое приложение Angular CLI. После настройки я проверил запись производительности и заметил явную нехватку прогонов обнаружения изменений.

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

Но почему?

Важно понимать, как работают ваши инструменты

Войдите в Zone.js. Я призываю любого пользователя Angular объяснить, как работает Zone.js и как Angular использует зоны.

Вот краткая версия: Zone.js изменяет прототипы всех асинхронных вызовов в API-интерфейсе браузера (setTimeout и т. Д.), Так что а) контекст может быть разделен между стеками вызовов и б) что хуки могут генерироваться в конце микро, макрос и событийные задачи.

Прохладный. А угловой?

Angular создает новую зону под названием NgZone, которая является дочерней по отношению к корневой зоне. Таким образом, когда эти хуки завершения задачи генерируются, angular может запускать обнаружение изменений на случай, если вызов привел к чему-то, где-то, изменению. Что прекрасно, когда у вас нет тонны компонентов. Не так много, когда у вас много.

И я не сбиваю с толку обнаружение изменений Angular. учитывая размер нашего приложения, оно действительно выполняло впечатляющую работу.

Эта проблема связана с выделением памяти, необходимой для его запуска. В итоге мы получали сборку мусора по 12 Мб каждые 900 мс.

Хорошо, а PixiJS?

Любой прослушиватель событий, который вы регистрируете для объекта PixiJS, не попадает в API браузера. Он затрагивает пользовательскую обработку событий PixiJS через их InteractionManager. И именно InteractionManager регистрирует событие pointermove в документе ... который был исправлен Zone ... который был разветвлен Angular.

Ok. Таким образом, наша сторонняя библиотека запускает обнаружение изменений в нашей структуре при перемещении мыши - еще одна проблема - но это все еще не объясняет, что запускает CD на каждом кадре анимации.

А теперь серьезно, важно понимать, как работают ваши инструменты

В PixiJS есть отличный способ обработки сценария DisplayObject анимации мимо вашей мыши. В нормальных условиях вы не получите mouseover событие, потому что мышь не двигалась, следовательно, нет события.

Однако PixiJS умен. Это событие pointermove используется для кеширования последнего события указателя. Затем на следующем тике цикла обработки событий pixi он проверит положение движущегося DisplayObject по отношению к местоположению события кэшированного указателя и инициирует событие mouseover, если экранный объект находится под курсором мыши.

Хорошо, но следующий тик цикла событий Pixi?

Оказывается, для анимации всех их DisplayObjects у них есть общий тикер ... подождите ... как обратный вызов в requestAnimationFrame.

Итак, теперь у нас есть не только цикл рендеринга, который выполняется 60 раз в секунду, но и тикер Pixi, запускающий обнаружение изменений 60 раз в секунду. Не круто.

Первый приз - заставить PixiJS работать в корневой зоне вместо Angular's NgZone, но тем временем эти 3 строки кода останавливают все попытки обнаружения ненужных изменений:

const ticker = Pixi.shared.ticker;
ticker.autoStart = false;
ticker.stop();

Теперь у нас есть 1/3 сборок мусора, как и раньше.