Основные различия между чистыми и грязными pipes в Angular и почему это так важно

Основные различия между чистыми и грязными pipes в Angular и почему это так важно

От автора: основные различия между чистыми и грязными pipes в Angular и почему это так важно — об этом идет речь в данной статье.

 

Основные различия между чистыми и грязными pipes в Angular и почему это так важно

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

Angular имеет довольно хорошую документацию по пайпам. Найти ее можно по ссылке. Но как часто бывает в документациях, в ней отсутствует четкое разделение. В этой статье я заполню пробелы и продемонстрирую вам разницу с точки зрения функционального программирования, которое показывает, откуда пошла идея чистых и грязных Angular pipes. Помимо различий вы узнаете, как разные пайпы влияют на производительность, а это уже поможет вам писать эффективные и производительные пайпы.

Чистая функция

В сети полно информации по функциональному программированию, и почти каждый разработчик знает, что такое чистая функция. Лично я определяю ее, как функцию, не имеющую внутреннего состояния. Это значит, что все выполняемые операции не зависят от этого состояния и задают одни и те же входные параметры, а также возвращают одно и то же заданное значение.

Ниже представлено 2 версии функции, которая складывает числа. Первая чистая, вторая грязная:

Если вызвать обе функции с одинаковыми входными параметрами, например, число 1, первая будет давать одинаковый результат при каждом вызове:

А вторая будет давать разный результат:

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

Разберем еще одно интересное следствие того, что у функции есть состояние. Скажем, у вас есть объект calculator, который принимает в качестве параметра функцию по сложению чисел и использует ее для вычислений:

Если функция чистая и не имеет состояния, ее можно свободно распространять на множество объектов класса Calculator:

Грязную функцию нельзя распространять. Потому что операции, выполняемые одним объектом Calculator будут влиять на состояние функции и, следовательно, на результат операций другого объекта Calculator:

Посмотрите, первый вызов метода add у второго объекта возвращает не 2, а 4. Давайте вспомним, что мы узнали о функциях. Чистые:

Значения входных параметров определяют результат, и если входные параметры не меняются, то и результат не изменится

Их можно распространять на множество объектов, не влияя на результат

Грязные:

С помощью входных значений нельзя определить, изменится выход или нет

Нельзя распространять, так как внутреннее состояние может быть изменено снаружи

Применение знаний к пайпам Angular

Предположим, мы создали 1 пайп и сделали его чистым:

И используем его следующим образом в шаблоне компонента:

Пайп чистый, это значит, что у него нет внутреннего состояния, и им можно делиться. Как сделать это в Angular? Несмотря на 2 записи в шаблоне, Angular может создать только один объект пайпа, который можно делить между использованиями. Кто читал мои предыдущие статьи «что такое component factory», знает, что ниже показан скомпилированный код, определяющий один пайп:

Который распространяется в функции updateRenderer:

С помощью функции unwrapValue вытягивается текущее значение пайпа с помощью вызова transform на нем. Объект пайпа ссылается по индексу узла в вызове функции nodeValue — в нашем случае это 0.

Однако если сделать наш пайп грязным и добавить в него некое внутреннее состояние:

Мы не хотим, чтобы первый вызов пайпа повлиял на второй, поэтому Angular создает 2 объекта пайпа, каждый со своим состоянием:

И он не распространяется в функции updateRenderer:

Посмотрите, теперь вместо узлового индекса 0 для каждого вызова Angular использует разные узловые индексы – 4 и 8 соответственно.

Второй вывод из первой главы: в чистых функциях мы с помощью входных значений можем определить, изменится выход или нет, а в грязных функциях мы этого не можем гарантировать. В Angular входные параметры передаются в пайп следующим образом:

Если пайп чистый, мы точно знаем, что его выход (через метод transform) строго задается его входными параметрами. Если вход не меняется, выход также не меняется. Такой подход позволяет Angular оптимизировать пайп и вызывать метод transform только при изменении входных параметров.

Однако если пайп грязный и имеет внутреннее состояние, то те же самые параметры не гарантируют нам тот же выход, что уже было показано на пример грязной функции addFn в первой главе. То есть Angular вынужден вызывать transform функцию на объект пайпа при каждом дайджесте.

AsyncPipe из пакета @angular/common отличный пример грязного пайпа. В него есть внутреннее состояние, которое содержит базовую подписку, созданную путем подписки на observable, переданный в пайп в качестве параметра. Поэтому Angular необходимо создать новый объект для каждого вызова пайпа, чтобы разные observables не влияли друг на друга. Также ему необходимо вызывать метод transform на каждый дайджест, так как даже если параметр observable может не меняться, через этот observable может приехать новое значение, которое необходимо обработать через обнаружение изменений.

Еще два грязных пайпа JsonPipe и SlicePipe. Оба пайпа необходимо переоценивать на каждый дайджест, так как у них есть внутреннее состояние, которое хранит объекты, которые могут мутировать без изменения ссылок на объекты (параметр пайпа не меняется).

Остальные стандартные пайпы в Angular чистые.

Заключение

Как мы увидели, грязные пайпы могут оказать значительное воздействие на производительность, если с ними неаккуратно обращаться. Снижение производительности может быть вызвано тем, что Angular создает несколько объектов на грязный пайп и вызывает метод transform на каждый цикл дайджеста.

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

Автор: Maxim Koretskyi

Источник: //blog.angularindepth.com

Редакция: Команда webformyself.

Метки:

Похожие статьи:

Комментарии Вконтакте: