У Angular есть свои фишки и причуды. В то время как React реализует модель однонаправленного потока данных по умолчанию, Angular с самого начала точно не следует этому пути.
Однонаправленный поток данных — это концептуальная модель, используемая многими фреймворками и библиотеками фронтэнда и за последние несколько лет набравшая популярность, благодаря появлению Redux шаблонов.
Но что же такое однонаправленный поток данных? Какое отношение он имеет к неизменности и почему так хорош в Angular?
Нижний уровень однонаправленного потока данных
“Однонаправленный поток данных” — не из тех выражений, которые мы часто видим или которые естественным образом всплывают в беседах. Тем не менее эта аккуратная небольшая модель может упростить реакцию приложений на изменения.
Для большинства приложений все в конечном итоге сводится к данным. Поток данных между различными уровнями, такими как представления, компоненты и сервисы, может определять эффективность модульного принципа приложения по мере его роста.
Так что же такое однонаправленный поток данных?
Однонаправленный поток данных — это программный шаблон, работающий с тем, как обновляются данные. В настоящее время используется два шаблона — однонаправленный и двунаправленный. Часть одно- указывает на то, что данные могут передаваться только в одном направлении.
Для фронтэнда, когда представление уже исполнено, необходимо действие для изменения данных и повторного исполнения представления полностью или его части.
React делает это по умолчанию, и не существует доступной привязки, позволяющей обновлять представление без прямого действия со стороны нижестоящих уровней.
Обновления происходят только в одном направлении. Данные не должны изменяться, потому считаются неизменными. Когда что-то происходит, возникают изменения, общее состояние системы больше не существует, система полностью рассматривается как новый объект.
Изменения вызываются внешними действиями. Когда это происходит, представление очищается и повторно исполняется с новыми данными. В двух словах: данные передаются только в одном направлении и не могут вернуться туда, откуда только что появились.
При однонаправленном потоке данных нет необходимости отслеживать изменения состояния, потому что изменения данных приведут к полному изменению представления.
Чтобы понять это в контексте Angular, нам нужно вернуться назад во времени к исходной версии Angular.js
Немного истории
В Angular обновления привязываются к представлению во время обнаружения изменений. Когда изменение обнаружено, изменения в дочернем элементе потенциально могут изменить и родительский, а не оставаться в изоляции. Это похоже на реализацию модели обратного наследования, которая позволяет родителям получать значения и свойства детей.
На поверхности изменения в представлении могут появляться одинаково и в однонаправленном, и в двунаправленном потоках. Тем не менее на уровне взаимодействия данных двунаправленный подход может быть проблематичным.
В Redux данные хранятся в едином логическом пространстве со стандартизованным методом доступа и изменения. Действия необходимы, чтобы сделать что-то с данными или получить их. Этот шаблон эффективен и становится все популярнее потому, что данные централизованы и могут быть изменены в одном месте, а не изменяются несколькими компонентами.
В Angular.js (Angular 1) двусторонняя привязка упрощала перемещение данных между частями приложения. Однако это создавало большую проблему, когда компоненты начинали действовать нежелательным образом из-за побочных эффектов двунаправленного потока.
На диаграмме выше — гипотетическое приложение с родительским компонентом, отображающим два дочерних. Переменная cat
вызывается фронтэндом и исполняется. В данном примере ее значение Tibbers
. Но с двунаправленным потоком данных все может запутаться.
На диаграмме выше, когда переменная cat
изменяется на Bob
, данные возвращаются тем же путем, создавая обратный каскадный эффект для отображаемых данных. Возможно, вы не хотели, чтобы родители дочернего элемента Child 2
обновляли данные.
Вот расширенный пример, когда двунаправленный поток данных приводит к беспорядку с ростом приложения.
На диаграмме выше изменение Child 2
влияет на оба родительских компонента, даже если они независимы друг от друга. Изолированные данные становятся взаимосвязанными. И это проблема, потому что по мере роста приложения возникает все больше потенциальных точек изменения и, соответственно, больше потенциальных точек отказа.
С однонаправленным подходом каждый компонент отделен от другого, состояние становится контейнерным. Невозможность обратного движения данных создает линейный путь для изменений.
Хотя двунаправленный подход и упрощает жизнь, потому что вам не нужно провоцировать изменения, он не так легко масштабируем, как однонаправленный.
Но разве нельзя использовать двунаправленность в Angular 2+?
Больше нет! Двусторонняя привязка — это не двунаправленный поток данных. Angular 2+ сейчас реализует однонаправленный поток данных, что означает, что представление не может обновляться после его компоновки.
Каждый раз, когда представление исполняется, Angular проходит жизненный цикл создания, проверки, компоновки и уничтожения представления. То есть представление фактически перерисовывается каждый раз, когда что-то меняется.
Мы не замечаем этих процессов, потому что они происходят очень быстро, и изменения воспринимаются почти мгновенно.
Двустороннее связывание относится к связи между службой и связанным с ней представлением. Двунаправленный и однонаправленный потоки данных относятся к границам, доменам и направлениям, в которых данные движутся между службами и представлениями.
Связывание относится к индивидуальным связям один-к-одному-к-одному, в то время как двунаправленность и однонаправленность отсылают к отношениям компонентов.
Заключение
Направление потока данных зачастую путают с концепцией двустороннего связывания. Это происходит потому, что оба явления связаны с данными.
Однонаправленный поток больше связан с тем, что можно делать с данными и как они взаимодействуют с другими компонентами, основываясь на отношениях между ними. Двустороннее связывание более ограничено представлением и его взаимодействием со связанными сервисными компонентами.
Понимание того, как работает поток данных, поможет вам разобраться, почему что-то идет не так и где именно. Реализация однонаправленного потока данных в Angular означает, что наследовать могут только дочерние компоненты, родительские больше не обладают этим правом.
Иногда из-за этого могут возникать проблемы, например, вам нужно, чтобы данные двигались в обратном направлении. Вот почему шаблон redux часто используется для централизации данных, при этом поддерживая однонаправленный поток данных.
С Redux данные отделяются от компонента и помещаются в единое пространство со стандартизованным методом поиска и изменения. Это означает, что наследование обратного потока больше не является проблемой, так как данные извлекаются из изолированного хранилища, а не из дочерних.
Это также означает, что компоненты могут совместно использовать данные без необходимости нарушать правила границ и передавать данные. Нет необходимости в трассировке, нужно только подключение к центральному хранилищу.
Читайте также:
- React.js за 5 минут
- Переиспользование форм в Angular
- Платформы Angular в деталях. Часть 1. Что такое платформы Angular?
Перевод статьи Aphinya Dechalert: The Tale of Unidirectional Dataflow in Angular