Найти тему
Nuances of programming

Повесть об однонаправленном потоке данных в Angular

Источник: Nuances of Programming

У Angular есть свои фишки и причуды. В то время как React реализует модель однонаправленного потока данных по умолчанию, Angular с самого начала точно не следует этому пути.

Однонаправленный поток данных — это концептуальная модель, используемая многими фреймворками и библиотеками фронтэнда и за последние несколько лет набравшая популярность, благодаря появлению Redux шаблонов.

Но что же такое однонаправленный поток данных? Какое отношение он имеет к неизменности и почему так хорош в Angular?

Нижний уровень однонаправленного потока данных 

“Однонаправленный поток данных” — не из тех выражений, которые мы часто видим или которые естественным образом всплывают в беседах. Тем не менее эта аккуратная небольшая модель может упростить реакцию приложений на изменения. 

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

Так что же такое однонаправленный поток данных? 

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

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

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

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

Изменения вызываются внешними действиями. Когда это происходит, представление очищается и повторно исполняется с новыми данными. В двух словах: данные передаются только в одном направлении и не могут вернуться туда, откуда только что появились.

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

Гипотетический однонаправленный поток данных
Гипотетический однонаправленный поток данных

Чтобы понять это в контексте Angular, нам нужно вернуться назад во времени к исходной версии Angular.js

Немного истории

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

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

Гипотетический однонаправленный поток данных
Гипотетический однонаправленный поток данных

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

В Angular.js (Angular 1) двусторонняя привязка упрощала перемещение данных между частями приложения. Однако это создавало большую проблему, когда компоненты начинали действовать нежелательным образом из-за побочных эффектов двунаправленного потока.

Гипотетическая диаграмма компонентов потока данных Angular.js
Гипотетическая диаграмма компонентов потока данных Angular.js

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

Гипотетическая диаграмма проблем двунаправленного потока данных
Гипотетическая диаграмма проблем двунаправленного потока данных

На диаграмме выше, когда переменная cat изменяется на Bob, данные возвращаются тем же путем, создавая обратный каскадный эффект для отображаемых данных. Возможно, вы не хотели, чтобы родители дочернего элемента Child 2 обновляли данные. 

Вот расширенный пример, когда двунаправленный поток данных приводит к беспорядку с ростом приложения. 

Гипотетический пузырьковый эффект при двунаправленном потоке данных
Гипотетический пузырьковый эффект при двунаправленном потоке данных

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

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

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

Но разве нельзя использовать двунаправленность в Angular 2+?

Больше нет! Двусторонняя привязка — это не двунаправленный поток данных. Angular 2+ сейчас реализует однонаправленный поток данных, что означает, что представление не может обновляться после его компоновки. 

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

Нижний уровень жизненного цикла Angular
Нижний уровень жизненного цикла Angular

Мы не замечаем этих процессов, потому что они происходят очень быстро, и изменения воспринимаются почти мгновенно. 

Двустороннее связывание относится к связи между службой и связанным с ней представлением. Двунаправленный и однонаправленный потоки данных относятся к границам, доменам и направлениям, в которых данные движутся между службами и представлениями. 

Связывание относится к индивидуальным связям один-к-одному-к-одному, в то время как двунаправленность и однонаправленность отсылают к отношениям компонентов.

Концептуальная диаграмма сравнения двустороннего связывания, однонаправленного и двунаправленного потоков данных
Концептуальная диаграмма сравнения двустороннего связывания, однонаправленного и двунаправленного потоков данных

Заключение

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

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

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

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

С Redux данные отделяются от компонента и помещаются в единое пространство со стандартизованным методом поиска и изменения. Это означает, что наследование обратного потока больше не является проблемой, так как данные извлекаются из изолированного хранилища, а не из дочерних. 

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

Читайте также:

Читайте нас в телеграмме и vk

Перевод статьи Aphinya Dechalert: The Tale of Unidirectional Dataflow in Angular