Angular: понятное введение в NGRX
Вот список тем, которые мы будем обсуждать в этой статье:
- Что такое ngrx
- Преимущества использования ngrx
- Недостатки использования ngrx
- Когда использовать ngrx
- Действия, Редукторы, Селекторы, Хранилище и Эффекты
Продолжение статьи с примером использования: «Angular: пример использования NGRX».
Что такое NGRX
NGRX — это группа библиотек, «вдохновленных» шаблоном Redux, который, в свою очередь, «вдохновлен» шаблоном Flux. Проще говоря, это означает, что шаблон Redux является упрощенной версией Flux шаблона, а NGRX — angular/rxjs версией Redux шаблона.
Что я имею в виду под «angular/rxjs» версией redux… «angular» часть заключается в том, что ngrx — это библиотека для использования в приложениях angular. Часть «rxjs» заключается в том, что реализация ngrx работает вокруг потока rxjs. Это означает, что он работает с использованием наблюдаемых и различных наблюдаемых операторов, предоставляемых «rxjs».
Основной целью этой схемы является обеспечение предсказуемого состояния контейнера, основанного на трех основных принципах
Давайте рассмотрим три принципа модели Redux и укажем на наиболее важные преимущества, которые они дают.
Единственный источник правды
В случае с архитектурой redux/ngrx это означает, что состояние всего вашего приложения хранится в дереве объектов в пределах одного хранилища.
В одном хранилище? О хранилищах мы поговорим позже, но для общего понимания, они несут ответственность за сохранение состояния и применение к нему изменений, когда им говорят об этом (когда отправляется действие, мы также поговорим о них позже).
Преимущества наличия одного источника правды многочисленны, но для меня наиболее интересным (потому что это то, что будет влиять на любое angular приложение) является следующее:
- Когда вы создаете приложение Angular, обычно вы разделяете состояние и обрабатываете несколько сервисов. По мере того, как ваше приложение растет, отслеживать изменения вашего состояния, становится все сложнее и сложнее и в итоге оно становиться беспорядочным, его сложно отлаживать и поддерживать.Наличие единственного источника правды решает эту проблему, поскольку состояние обрабатывается только в одном объекте и в одном месте, поэтому отладка или добавление изменений становится намного проще.
Состояние read-only (только для чтения)
Вы никогда не измените состояние напрямую, вместо этого вы будете отправлять действия, которые описывают действия с объектом (это могут быть такие вещи, как получение, добавление, удаление или обновление состояния).
Отправить действие?.. Мы поговорим о действиях позже, но для общего понимания, это идентификаторы операции над вашим приложением, и они могут быть запущены (или отправлены), чтобы сообщить приложению выполнить операцию, которую представляет действие.Избегая обновлять состояние из разных мест и имея централизованное место для внесения изменений, которое реагирует на конкретные действия, вы получаете много преимуществ. Не говоря уже о самых важных:
- Вы знаете, что любое изменение в состоянии произойдет только в одном месте. Это оказывает большое влияние на отладку и тестирование.
- Вы знаете, что если посылается определенное действие, то операция в состоянии всегда одна и та же. Опять же, это напрямую влияет на отладку и тестирование.
Изменения вносятся с простыми функциями
Операция, инициируемая отправкой действия, будет простой функцией, называемой в рамках архитектуры redux, редукторами.
Эти редукторы (помните, что они простые функции) получают действие и состояние, в зависимости от отправленного действия (обычно с оператором switch), они выполняют операцию и возвращают объект нового состояния.
Состояние в redux приложении является неизменным! Поэтому, когда редуктор что-то меняет в состоянии, он возвращает новый объект состояния.
Преимущества использования чистых функций хорошо известны, как, например, тот факт, что они могут быть немедленно протестированы, если вы передадите те же аргументы, которые вы получите в результате.
Этот подход также позволяет нам перемещаться между различными экземплярами нашего состояния с помощью инструментов разработки Redux/ngrx и видеть, что изменилось между экземплярами и кто его изменил, помимо всего прочего. Таким образом, использование чистых функций и возвращение новых экземпляров состояния также имеет большое влияние на отладку.
Но главное преимущество, на мой взгляд, заключается в том, что, привязав все входные данные наших компонентов к свойствам состояния, мы можем изменить стратегию обнаружения изменений на push, и это повысит производительность приложения.
Отлично… Итак, каковы преимущества использования NGRX?
Мы уже упоминали большинство из них, когда говорили о принципах redux шаблона. Но давайте отметим наиболее важные преимущества использования redux шаблона в приложении (на мой взгляд):
- Поскольку у нас есть единый источник правды, и вы не можете напрямую изменить состояние, приложения будут работать более согласованно.
- Использование redux шаблона дает нам много интересных функций, облегчающих отладку.
- Тестирование приложений становится проще, поскольку мы вводим простые функции для обработки изменений состояния, а также потому, что оба, ngrx и rxjs, имеют множество замечательных возможностей для тестирования.
- Как только вы почувствуете себя комфортно при использовании ngrx, понимание потока данных в ваших приложениях станет невероятно простым и предсказуемым.
… и минусы
- У NGRX, конечно, есть кривая обучения. Она не большая, но и не такая уж и маленькая, и я думаю, что это требует некоторого опыта или глубокого понимания некоторых программных паттернов. Любой разработчик со средним стажем должен быть в порядке, но для младшего поначалу может быть немного запутанным.
- Для меня это кажется немного многословным. Но каждый раз, когда вы добавляете какое-либо свойство в состояние, вам нужно добавлять действия, диспетчеры, вам может потребоваться обновить или добавить селекторы, эффекты, если таковые имеются, обновить магазин. А также вы запускаете конвейерную (конкатенацию) rxjs операторов и наблюдаемых повсюду.
- NGRX не является частью угловых библиотек ядра и не поддерживается Google, по крайней мере, напрямую, потому что в команде Angular есть участники ngrx. Просто нужно кое-что обдумать, прежде чем вы добавляете библиотеку, которая будет большой зависимостью для вашего приложения.
Когда стоит использовать NGRX
Таким образом, по общему мнению, ngrx следует использовать в средних/больших проектах, когда управление состоянием начинает становиться сложным в обслуживании. Некоторые люди, более фанатичные, скажут что-то вроде «если у вас есть состояние, то у вас есть NGRX».
Я согласен, что его следует использовать в средних или крупных проектах, когда у вас есть значительное состояние и множество компонентов, использующих это состояние, но вы должны учитывать, что Angular сам по себе предоставляет множество решений для управления состоянием, и если у вас есть сильная команда разработчиков Angular, то, возможно, вам не нужно беспокоиться о ngrx.
При этом я считаю, что сильная команда разработчиков Angular может также решить включить ngrx в решение, потому что они знают всю мощь redux шаблона, а также силу добавляемую операторами rxjs, и они чувствуют себя комфортно, работая с обоими…
Если вы ожидали простого ответа, чтобы решить, когда использовать ngrx, вы не получите его и не доверяете никому, кто дает вам этот ответ, за пределами вашей организации или группы. Решение зависит от изучения плюсов и минусов, понимания вашей команды и учета их мнения.
NGRX Действия, Редукторы, Селекторы, Хранилище, и Эффекты
Это основные строительные блоки потока ngrx. Каждый из них берет на себя часть процесса начала операции по изменению нашего состояния и получению данных.
На изображении мы видим поток ngrx. Давайте объясню его…
- В наиболее распространенном сценарии все начинается с представления компонентов. Некоторые взаимодействия, осуществляемые пользователем, могут привести к тому, что компонент отправит действие.
Действия…
В объекте «Хранилище» имеется функция отправки (запуска) действий. Действия — это классы, реализующие интерфейс NGRX Action. У этих классов Action'ов есть два свойства (возьмем в качестве примера класс действия под названием GetUserName):
type (тип): это строка только для чтения, описывающая, что означает действие. Например: '[User] Get User Login''.
payload (полезная нагрузка): тип этого свойства зависит от того, какие данные нужно отправить редуктору. В случае предыдущего примера это будет строка, содержащая имя пользователя. Не все действия должны иметь полезную нагрузку.
- Если это действие не вызывает эффекта, редуктор будет анализировать это действие (обычно с помощью оператора switch) и возвращать новое состояние, которое будет результатом слияния старого состояния со значением, измененным вызовом этого действия.
Редуктор...
Редукторы — это простые функции, принимающие два аргумента: предыдущее состояние и Действие. При отправке Действие ngrx проходит через все редукторы, передающие в качестве аргументов предыдущее состояние и Действие, в том порядке, в котором редукторы были созданы, до тех пор, пока не найдет аргументы для этого действия.
- Если эффект запускается в результате отправки действия, то это происходит из-за того, что перед вызовом редуктора произойдут некоторые побочные эффекты. Вероятно, это может быть что-то вроде вызова службы HTTP для получения данных.
Эффекты...
После того, как эффект завершен (побочные эффекты закончены), новое действие «состояние-результат» выстреливает из эффекта (может быть, побочные эффекты успешны или неудачны).
Воздействие на экосистему библиотек ngrx позволяет нам иметь дело с побочными эффектами, вызванными отправкой действия за пределы угловых компонентов или хранилища ngrx.
Effects слушает, если какое-либо действие отправлено, то, подобно редукторам, он проверяет, является ли действие одним из тех типов действий, которые у него есть.
Затем выполняется побочный эффект, обычно получение или отправка данных в API.
В конце концов, редуктор будет выдавать еще одно действие, обычно относящееся к состоянию результата побочного эффекта (success, error и т.д.), затем редуктор войдет в сцену, о чем мы уже упоминали в потоке ngrx.
- Теперь в магазине появилось новое состояние. Состоянием может быть большое дерево объектов, поэтому ngrx вводит селекторы, чтобы иметь возможность использовать только те фрагменты объекта, которые нам нужны в конкретном компоненте.
Селекторы...
Как мы упоминали ранее, дерево состояний может стать довольно большим объектом, не имеет смысла размещать весь этот объект в тех местах, где нам нужна только его часть.
Хранилище NGRX предоставляет нам функцию «выбрать» для получения частей нашего хранилища. Но что, если нам нужно применить некоторую логику к этому срезу, прежде чем использовать данные в компонентах.
Здесь селекторы принимают меры. Они позволяют нам отделить любое преобразование данных среза состояния от компонентов. Функция выбора «store» принимает в качестве аргумента простую функцию, эта функция является нашим селектором.
Хранилище ...
Хранилище — это объект (экземпляр класса Store ngrx), который объединяет вещи, о которых мы упоминали ранее (действия, редукторы, селекторы). Например, когда отправляется действие (с использованием функции отправки объекта хранилища), хранилище является тем, которое находит и выполняет соответствующий редуктор.
Он также является держателем состоянием приложения.
Продолжение статьи с примерами использования: «Angular: пример использования NGRX».