Недавно я принял участие в Advanced Angular Workshop с Манфредом Штайером и Майклом Хладким в качестве тренеров. Они охватили множество тем, связанных с архитектурой корпоративных приложений, включая способ преобразования службы с отслеживанием состояния на основе запроса на реагирование. В этом сообщении в блоге я хотел бы описать процесс с моей точки зрения на простом значимом примере. В дополнение к решению, рассмотренному во время семинара, я представлю немного улучшенный, даже более реактивный способ достижения цели.

Вступление

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

Для простоты я буду использовать JSONplaceholder как поддельный REST API. Полученные данные пользователя соответствуют следующему интерфейсу:

Подход на основе вытягивания

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

Компонент Пользователь имеет следующую разметку:

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

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

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

У этого подхода есть несколько недостатков. Прежде всего, вам нужно обеспечить своего рода механизм проверки обновлений. В этом примере я полагаюсь на встроенный механизм обнаружения изменений Angular со стратегией по умолчанию. В результате каждая привязка оценивается после возникновения асинхронного события. Во-вторых, вы не можете использовать стратегию обнаружения изменений OnPush, если какой-либо другой компонент в другой области приложения также заинтересован в данных выбранного пользователя. И последнее, но не менее важное: если создание модели представления для выбранного пользователя требует некоторых вычислений (здесь я просто визуализирую ответ json), вычисление выполняется для каждого цикла обнаружения изменений. Это бесполезная трата, и вам следует либо извлечь код в запоминаемую функцию, либо сохранить проанализированные пользовательские данные в поле класса обслуживания вместо необработанного ответа (однако это не всегда хорошее решение). Следовательно, этого подхода может быть достаточно для простого небольшого приложения. Однако, если вы заботитесь о производительности и хотите избежать рендеринга устаревших данных, вам следует использовать подход, основанный на push!

Подход на основе push

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

Начнем с рефакторинга службы User:

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

Теперь в шаблоне компонента:

вы можете просто подписаться с помощью канала Async на поток user $, предоставляемый классом компонента:

Однако есть возможности для улучшения, поскольку вы можете отказаться от явной подписки в методе loadUser в пользу композиции потоков.

Состав потока

При использовании подхода на основе push вы всегда должны четко определять поток данных в своем приложении, а именно определять, что является источником / входом результирующих / выходных данных. В этом примере каждый раз, когда я меняю выбранного пользователя, я хочу получить данные пользователя с сервера. Таким образом, исходный поток - это поток выбранного идентификатора пользователя, тогда как полученный наблюдаемый объект содержит сведения о текущем пользователе. Давайте посмотрим, как вы можете определить выходной поток в терминах входного.

Код компонента (и шаблон, и класс) остается прежним. Единственное изменение происходит внутри службы User:

В приведенной выше реализации я просто вставляю идентификатор выбранного пользователя в частную тему, которая действует как исходный поток для наблюдаемого объекта user $. Каждый раз при изменении идентификатора пользователя выполняется новый HTTP-запрос.

В результате я избавился от явного вызова подписки в методе loadUser. Кроме того, я могу просто обрабатывать условия гонки с помощью оператора switchMap. С другой стороны, в реальном приложении вы должны позаботиться об обнаружении ошибок во внутреннем потоке (возвращенном оператором switchMap), чтобы долгоживущий user $ поток остается активным в течение всего времени жизни приложения. Многоадресная рассылка обеспечивается применением оператора shareReplay.

Выводы

В этом сообщении в блоге я представил два способа перехода от вытягивающего подхода к основанному на выталкивании. Решения на основе pull более необходимы, поэтому они могут быть хорошей отправной точкой для разработчиков, не знакомых с реактивным / декларативным программированием. Тем не менее, определенно стоит изучить реактивное программирование, поэтому вы должны стремиться к тому, чтобы ваши сервисы основывались на push-уведомлениях. Такой подход позволяет повысить производительность приложения и приводит к уменьшению количества ошибок в коде.

Надеюсь, пост вам понравился. Если да, то аплодируйте и следите за моим блогом :)