Добавим реактивности

КДВП

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

Что же такое — реактивность?

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

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

Пример реактивного блока на текущем сайте

Или еще пример — комментарии. Если вы читаете какую-то тему на форуме и кто-то в этот момент напишет новый комментарий, то вы увидите вот такую вот плашку, которая сразу сообщит вам об этом новом комментарии:

Реактивность в комментариях

Дальше — больше! Вы поставили лайк на комментарий? Мы можем сразу показывать новый рейтинг всем, кто сейчас находится на той же страничке. Отредактировали свой комментарий или тему на форуме — все уже видят ваши изменения. Стример поменял название трансляции — f5 больше не нужен. И так далее…

Теория

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

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

Model — это сами данные, которыми мы располагаем. Возьмем, например, стрим. У стрима есть различные свойства — название, игра, имя стримера, статус (онлайн/офлайн), количество зрителей и тд. Совокупность всех этих свойств для нас, грубо говоря, как раз является моделью Model.

ViewModel определяет взаимодействие отображения View и модели Model.

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

В этой статье я не буду углубляться в назначение каждого элемента в этой цепочке. Если вам это интересно — вы легко нагуглите описание этого паттерна проектирования по запросу MVVM.

Так вот. Как я уже сказал — реактивность вроде бы заложена в сам ангуляр, и при изменении, например, названия трансляции, движок сам позаботится об обновлении данных на странице. Но тут есть один нюанс. Дело в том, что модель на фротнэнде не обновляется сама при изменении данных на сервере. Связано это с односторонностью протокола HTTP, по которому мы получаем данные.

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

Практика

Первым делом возьмемся за бэкэнд. Напишем простенький trait, который можно будет вешать на любой объект, который мы захотим добавить в нашу реактивную систему:

После сохранения объекта в базу данных, наш фреймворк вызовет метод afterSave. Если на объект повешен этот самый новый трейт UpdateModel, то одним из действий после сохранения будет отправка новых данных на роутер Wamp, о котором я писал в прошлой статье.

Теперь нам нужно принять эти данные на стороне фронтэнда. Тут все немного интереснее.

В одной из прошлых статей я упоминал, что все данные, приходящие с бэкэнда, кэшируются. Для этого у нас есть специальный сервис, который называется ObjectHolder. Суть его очень проста — он может сохранять в себя какие-то данные и отдавать их по запросу. Но есть важная особенность — все эти данные связываются между собой. Рассмотрим этот момент на примере стримов:

Связывание данных через ObjectHolder

На главной страничке стримы Фомби и Винограда присутствуют в двух местах: селектор стримов и избранное.

Блоки селектора и избранного с точки зрения архитектуры сайта — два разных компонента и данные в эти компоненты поступают из разных апишек. По сути, это означает, что движок никак не связывает данные и для него стрим Фомби в разных блоках — это совершенно разные стримы. Именно поэтому на текущем сайте вы можете постоянно наблюдать вот такую картину:

Несвязанные объекты

Вот тут-то в дело и вступает ObjectHolder. Мы кладем данные из разных апишек в него и он автоматически связывает одинаковые объекты. Вуаля — теперь для ангуляра это один и тот же объект. Теперь, чтобы обновить какие-то данные у объекта нам достаточно обновить их в самом ObjectHolder и данные изменятся везде. Магия!

Вот как это выглядит в коде компонента избранного:

rxjs во всей красе

Лаконично, не правда ли? После того, как мы получили данные из API, мы кладем их в кэш и подписываемся на обновления объектов. Если какой-то из объектов обновился, то мы заново вызываем onUpdate. Здесь повторный вызов onUpdate нам нужен для того, чтобы подготовить данные для отображения (сортировки, фильтры и тд). В тех компонентах, где данные напрямую попадают во вьюшку, onObjectsUpdate не нужен.

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

Все просто — подписываемся на события объекта и если приходит обновление — обновляем данные в ObjectHolder.

Ладно, я немного слукавил. Выглядит все просто, потому что вся магия в cacheArrayOfObjects и в onObjectsUpdate:

Магия rxjs
Еще магии

Ну и там еще немного магии в самом ObjectHolder.

Заключение, которое больше похоже на введение и вообще оно странное.

Ненавижу обновлять страничку. Это похоже на “чтобы изменения вступили в силу, вам нужно перезагрузить компьютер”. Да идите нафиг, я не хочу ничего делать, хочу сразу видеть результат. Скажем НЕТ лишним действиям, скажем ДА отзывчивым и реактивным интерфейсам. Землю — крестьянам, фабрики — рабочим. Ура, товарищи!

Show your support

Clapping shows how much you appreciated Goodgame Dev’s story.