Что такое реактивный фреймворк
Исторически сложилось, что все браузеры, мобильные и десктопные, на всех платформах понимают всего лишь три вещи: HTML, CSS и JavaScript (не путать с Java!).
Были попытки подружить браузеры с другими технологиями: Visual Basic, Java, ActiveX, — но все они провалились, потому что производители железа и браузеров не смогли договориться об открытых стандартах. Остались только открытые стандарты, разрабатываемые публичными консорциумами и рабочими группами. Например, W3C разрабатывает HTML и CSS.
- HTML отвечает за структуру страницы.
- CSS — за ее оформление (визуальное и адаптив для разных экранов).
- JavaScript — за взаимодействие страницы с пользователем (по изначальной задумке; сейчас-то уже практически вообще за все).
Каждая технология развивается независимо, у каждой есть несколько версий. Самые свежие на сегодня версии: HTML5, CSS3, ECMAScript 2018 (это стандарт JavaScript).
Браузеры тоже развиваются независимо. Кто-то (то Internet Explorer, то Safari) отстает от стандартов, кто-то (обычно Chrome или Firefox) бежит впереди и внедряет экспериментальные фичи.
Отсюда постоянная головная боль фронтенд-разработчиков: сайт должен выглядеть во всех браузерах одинаково (причем именно так, как его придумал дизайнер). Плюс работать быстро, безопасно и в соответствии со спецификацией.
Вдобавок HTML и CSS — это не языки программирования, а языки разметки: один лишь синтаксис, набор команд — конструкций и правил — для представления содержимого страницы. Ну, к примеру, в них нет как таковых классов, объектов, функций, методов, присущих языкам программирования.
Чтобы наделить веб-сайт функциональностью и бизнес-логикой, приходится подключать язык программирования на стороне сервера или на стороне клиента (в браузере), а чаще всего — и там и там.
Стандартный Javascript — не самый эффективный и приятный для работы язык программирования. Специалисты критикуют его за то, что даже для самых простых операций приходится писать очень много строчек кода, постоянно повторяться.
Это замедляет разработку. Потому верстальщики и программисты ищут способы использовать продвинутые инструменты вместо «чистых» JavaScript, HTML и CSS.
Пример с таблицей
Чтобы проиллюстрировать пример неэффективности всей этой связки, возьмем такую простую конструкцию, как таблица.
В HTML есть набор тегов для создания таблицы (основные из них — table, th, tr, td). С их помощью можно создать только сетку таблицы с минимальными настройками внешнего вида: задать ширину колонок, размеры ячеек и в принципе всё.
Добавив CSS, можно (изрядно помучавшись) придать ей пристойный вид и при должном старании адаптировать для разных экранов. Но мы все равно не сможем сортировать таблицу по одной или нескольким колонкам, показать порядок сортировки, добавлять или удалять колонки и столбцы, перетаскивать данные из одной ячейки в другую, использовать формулы для подсчета сумм по строкам и столбцам, применить условное форматирование ячеек (подсветить отрицательные, например) и т. п.
Всё это на HTML и CSS невозможно сделать, потому что в HTML нет такого объекта, как таблица, и нет методов работы с ней, которые поддерживались бы любым браузером. Разработчики стандарта 20 лет назад не предполагали, что пользователи захотят работать с содержимым веб-страницы.
Для всего этого в HTML и CSS нет подходящих решений, потому на помощь приходит JavaScript — язык программирования, который может манипулировать объектами в структуре HTML и применять к ним стили CSS.
Когда-то для этого использовали другие языки (Visual Basic, например), пытались применять другие технологии (Adobe Flash, например), но JavaScript всех вытеснил. И не в последнюю очередь благодаря JavaScript-библиотекам.
Библиотеки
Каждый раз писать код на JavaScript, чтобы сделать ту же сортировку таблиц, — это, разумеется, не подход джедая.
И появились библиотеки — наборы готовых функций на JavaScript, выполняющие типовые операции с HTML-кодом страницы. Пример такой библиотеки — jQuery.
Этих библиотек за двадцать с лишним лет существования JavaScript появилось великое множество. Программистам приходилось комбинировать библиотеки, дружить их между собой, обновлять (ведь каждая развивается своим чередом), следить за совместимостью. Да еще самим код писать — не все же есть в библиотеках.
Подход с библиотеками до сих пор живет. В небольших проектах достаточно подключить одну-две библиотеки для конкретных улучшений. Например, чтобы рисовать красивые графики, подключаем бесплатный Chart.js или платный AmCharts. Если нужна анимация и отзывчивость интерфейса — тот же jQuery, для работы с элементами интерфейса есть смысл взглянуть на Sencha Ext JS и т. п.
И тут появились фреймворки
Для HTML+CSS тоже стали появляться подобные «полуфабрикаты» — заготовки из кусков кода, которые решают типовые проблемы верстки. Например, многоколоночная верстка, закрепленный на странице хедер или футер и прочие типовые задачи, которые выгоднее решить один раз, а потом применять в новых проектах.
Так появились первые фронтенд-каркасы разработки, или фреймворки.
Почему они не библиотеки? Потому что это не набор готовых функций, которые можно добавить к проекту и использовать точечно на отдельных страницах.
Каркас (фреймворк) предполагает, что весь проект будет следовать заданной им структуре. То есть он задает ограничения, которых нужно придерживаться, чтобы ускорить разработку, точнее следовать стандартам, снизить порог вхождения разработчиков в проект и т. п.
Самый известный образец HTML+CSS-фреймворка — Bootstrap (вот примеры, вот один из компонентов — карусель, вот другой — кнопки).
Важно, что все сделано на стандартных HTML и CSS и будет работать (и работать более или менее одинаково) во всех браузерах.
Фреймворки уже содержат подогнанные друг к другу совместимые библиотеки, так что разработчику не нужно ничего обновлять, помнить про ограничения и заботиться о совместимости. Так, многие компоненты Bootstrap содержат код на JavaScript с использованием библиотеки jQuery.
Другой известный фреймворк, Foundation, кроме jQuery использует библиотеки Modernizr и FastClick.
Два фреймворка в приложении будут конкурировать за базовые вещи. К примеру, один захочет 12-колоночную сетку, другой — 16-колоночную; они могут использовать одинаковые названия методов JavaScript для разных целей и т. д.
Поэтому между собой фреймворки несовместимы: нужно определиться и выбрать один. Если у вас проект на Bootstrap, а вам нужны вот такие вот звездочки из Foundation — то поженить их не получится. (Но можно самому запилить звездочки для Bootstrap и поделиться ими как плагином. Конечно же, кто-то это уже сделал.)
Так вот, идем дальше
Главная проблема современного веба — разрозненность технологического стека. Невозможно выучить все технологии, знать и уметь их правильно готовить.
Программисты в какой-то момент поделились на фронтенд и бэкенд. А еще необходимы дизайнеры и верстальщики, не говоря уже про админов БД, сисадминов, архитекторов и прочих важных и нужных людей.
В общем, для разработки одного приложения требуется столько разных специалистов, что возникло вполне естественное желание как-то объединить некоторые технологии. Если гомогенизировать технологический стек — расширить набор HTML-компонент и тесно подружить их с бэкендом — то фулстек-программист сможет взять на себя всю разработку, от хранения данных до обработки пользовательского взаимодействия.
В то время (речь сейчас идет о передовой мысли десятилетней давности) страницу из структуры и данных собирал сервер. Брал данные из БД, брал шаблон страницы, заполнял его, показывал нужные пользователю контролы и отправлял браузеру.
Стали появляться серверные фреймворки, позволяющие писать на бэкенде код, автоматически генерирующий верстку:
В условиях растущей потребности в надежных и интерактивных веб-интерфейсах, многие разработчики стали применять парадигму реактивного программирования.
Прежде чем мы приступим к реализации собственного реактивного фреймворка, вкратце объясним, что такое реактивное программирование. Википедия дает нам классический пример реализации реактивного интерфейса, а именно электронной таблицы. Определение формулы, такой как = A1 + B1 , будет обновлять ячейку при изменении либо A1 , либо B1 . Такая формула может рассматриваться как вычисляемое значение.
Вы узнаете, к ак реализовать вычисляемые значения во второй части этой серии статей. Но сначала нам нужна основа для нашего реактивного фреймворка.
В настоящее время существует множество различных подходов к решению проблемы наблюдения и реагирования на изменяющееся состояние приложения.
- У Angular 1.x есть так называемая «грязная» проверка (dirty checking).
- React, из-за того, как он работает, фактически не отслеживает изменения в модели данных. Он использует виртуальный DOM для сравнения изменений и исправления DOM.
- Cycle.js и Angular 2 предпочитают реализации реактивных потоков, такие как XStream и Rxjs.
- Библиотеки, такие как Vue.js, MobX или Ractive.js, используют вариацию геттеров/сеттеров для создания наблюдаемых моделей данных.
В данном статье мы рассмотрим способ геттеров и сеттеров для отслеживания изменений и реагирования на них.
Примечание: чтобы максимально упростить учебное руководство, в коде отсутствует поддержка ссылочных типов данных или вложенных свойств и многих необходимых проверок на работоспособность, поэтому этот код ни в коем случае нельзя считать готовым к выпуску. Код ниже написан с использованием стандарта ES2015 и немного вдохновлен реализацией реактивного движка Vue.js.
Наблюдаемый объект
Давайте начнем с объекта data , свойства которого мы хотим отслеживать.
Создадим две функции, которые будут преобразовывать свойства нашего объекта в наблюдаемые свойства, используя возможности геттеров и сеттеров.
Запустив observeData(data) , мы преобразуем наш объект в объект, который можно отслеживать. Теперь у нас есть способ создания уведомлений при каждом изменении значений свойств объекта.
Реакция на изменения
Прежде чем мы начнем отправлять оповещения, нам нужно что-то, о чем мы можем уведомить. Это прекрасный пример, когда мы можем использовать шаблон Наблюдатель. В данном случае мы будем использовать реализацию сигналов.
Создадим функцию observe .
Теперь вернемся к функции notify , которую вы видели раньше.
Как вы можете видеть, теперь каждый раз, когда изменяется одно из свойств, вызываются назначенные обработчики.
Итак, давайте превратим все это в фабричную функцию, принимающую объект данных, который должен быть реактивным. Я назову ее как Seer . В итоге мы получаем что-то вроде этого:
Все, что нам нужно сделать, это создать новый реактивный объект. Благодаря открытым функциям notify и observe мы можем наблюдать и реагировать на изменения, внесенные в объект.
Просто, не так ли? Теперь, когда у нас есть базовая технология для реактивности, давайте ее применим. Я упомянул, что с реактивным подходом к front-end-разработке мы не должны беспокоиться о том, как вручную обновлять DOM после каждого изменения.
Есть много подходов к этому. Я думаю, что наиболее актуальным сейчас является так называемый виртуальный DOM. Если вам интересно узнать, как создать свою собственную реализацию виртуального DOM, для этого есть замечательные руководства (Прим. перев.: в оригинале нет никаких ссылок на подобные руководства, поэтому приведу то, что я нашел сам). Однако здесь мы рассмотрим более простой подход.
Предположим наш HTML выглядит так: <h1>Title comes here</h1> .
Функция, отвечающая за обновление DOM, будет выглядеть так:
Это будет работать, но на самом деле требует от нас большой работы, чтобы фактически привязать все элементы DOM к желаемым моделям данных.
Вот почему мы можем пойти дальше и автоматизировать все это. Если вы знакомы с AngularJS или Vue.js, вы наверняка должны вспомнить использование пользовательских HTML-атрибутов, таких как ng-bind или v-text . Мы создадим нечто подобное! Наш пользовательский атрибут будет называться s-text . Мы будем искать его для создания привязок между DOM и моделью данных.
Давайте обновим наш HTML:
title —это свойство, значение которого мы хотим отображать внутри элемента <h1> .
Резюме
Теперь, когда у нас есть способ разобрать DOM и привязать узлы к модели данных, давайте добавим эти две функции в фабричную функцию Seer , где мы будем разбирать DOM при инициализации.
Результат должен выглядеть так:
Пример на JSFiddle:
Продолжение следует…
Это первая часть серии о создании собственного реактивного фреймворка.
Следующая часть будет посвящена созданию вычисляемых свойств, каждое из которых имеет свои отслеживаемые зависимости.
Ваши отзывы и идеи о том, что нужно освещать в дальнейшем, приветствуются в комментариях (Прим. перевод.: в комментариях к оригинальному посту :-))!
Как только ООП-языки доросли до массового применения, разработчики осознали, насколько иногда не хватает возможностей С-подобных языков. Поскольку написание кода в стиле функционального программирования серьезно разрушает качество ООП-кода, а значит, и поддерживаемость проекта, был придуман гибрид — реактивное программирование.
Парадигма реактивной разработки строится на идее постоянного отслеживания изменений состояния объекта. Если такие изменения произошли, то все заинтересованные объекты должны получить уже обновленные данные и работать только с ними, забыв про старые.
Хорошим примером идеи реактивного программирования может служить Excel-таблица. Если связать несколько ячеек одной формулой, результат вычисления будет меняться каждый раз, когда изменятся данные в этих ячейках. Для бухгалтерии такое динамическое изменение данных — привычное дело, но для программистов это скорее исключение.
В этом примере функции F1 и F2 будут работать с разными значениями переменной C. Часто требуется, чтобы у обеих функций были только самые актуальные данные, — реактивное программирование позволит без изменения логики самих функций сразу же вызвать F1 с новыми параметрами. Такое построение кода дает приложению возможность моментально реагировать на любые изменения, что сделает его быстрым, гибким и отзывчивым.
ReactiveX
Воплощать с нуля идеи реактивного программирования может быть довольно хлопотно — есть подводные камни, да и времени это займет прилично. Поэтому для многих разработчиков эта парадигма оставалась только теоретическим материалом, пока не появился ReactiveX.
Фреймворк ReactiveX — это инструмент для реактивного программирования, работающий со всеми популярными ООП-языками. Сами создатели называют его мультиплатформенным API для асинхронной разработки, основанным на паттерне «Наблюдатель» (Observer).
Если термин «реактивное программирование» — это своего рода теоретическая модель, то паттерн «Наблюдатель» — готовый механизм отслеживания изменений в программе. А отслеживать их приходится довольно часто: загрузку и обновление данных, оповещения о событиях и так далее.
Рис. 1. Паттерн «Наблюдатель»
Собственно, создатели ReactiveX не придумали ничего революционного, они просто удобно реализовали паттерн. И хотя во многих ООП-языках, и в Java в частности, есть готовые реализации паттерна, в этом фреймворке присутствует дополнительный «тюнинг», который превращает «Наблюдатель» в очень мощный инструмент.
RxAndroid
Порт библиотеки ReactiveX для мира Android называется rxAndroid и подключается, как всегда, через Gradle.
Все готово для работы. Осталось связать объекты между собой — и «Hello, world!» в реактивном программировании готов!
Надо сказать, что это был очень простой пример. В ReactiveX есть множество вариантов поведения всех участников паттерна: фильтрация, группирование, обработка ошибок. Пользу от реактивного программирования можно ощутить, только попробовав его в деле. Приступим к задаче посерьезнее.
Академические примеры работы ReactiveX ты найдешь на официальном сайте фреймворка, а в этой статье мы с тобой разберем более приземленный пример.
Как показывает практика, создавая что-то сложнее студенческого курсовика, следует строить приложение с использованием еще одного паттерна — MVP (Model, View, Presenter). Он позволяет разбить ООП-проект на отдельные, слабо связанные и легко заменяемые логические блоки кода.
Как правило, каждый из блоков в MVP имеет свое предназначение — это помогает использовать паттерн с полной эффективностью.
- Model — поставщик данных в приложении. Внутри него аккумулируется вся механика запросов: сетевое взаимодействие, работа с файлами и прочее.
- View отвечает за UI и все, что рядом. В этом блоке генерируются запросы на выдачу необходимых данных и, при желании, ведется их финальная обработка: сортировка, выборка отдельных значений и так далее.
- Presenter действует как посредник. Поскольку View и Model мало что знают друг и о друге, этот блок служит своего рода «переходником», перенаправляя запросы от одного к другому.
ООП-код обязательно нужно стараться делать поддерживаемым, иначе программе сложно будет дожить хотя бы до версии 1.1. Разработчику часто приходится вносить изменения уже во время рабочего процесса: добавлять кеширование, изменять дизайн, делать новое меню и так далее. Следование принципам MVP помогает изменять логику приложения практически без боли и потери времени.
При этом редкое приложение обходится без длительных вычислений, а значит, разработчику придется еще и как-то управляться с дополнительными потоками. Наш журнал уже не раз затрагивал эту тему, и ты должен быть в курсе возможных проблем: взаимные блокировки, перерасход памяти, потеря результатов. ReactiveX позволяет не только легко внедрить паттерн «Наблюдатель», но и раскидать вычисления по разным потокам, причем реализацию многопоточности он берет на себя.
Для демонстрации сказанного создадим небольшой проект, в котором код построен в соответствии с паттерном MVP, а все тяжелые вычисления делегированы фреймворку. Начнем с блока Model. При использовании MVP нужно стремиться передавать как можно меньше данных между логическими блоками. Достаточно ограничиться, к примеру, ссылкой на ресурс для доступа к данным.
Источников, генерирующих данные, наверняка окажется несколько, поэтому внутри Model будет объект класса HeavyOperation.
Как понятно из названия, HeavyOperation будет работать очень долго, а в Android длительные операции нужно всегда выполнять в отдельном потоке.
Кстати, некоторые библиотеки самостоятельно решают такие проблемы: к примеру, при загрузке файла с помощью Retrofit не надо что-то придумывать, она сама загрузит файл в новом потоке. А когда нужно будет работать с файловой системой или базой данных, поможет ReactiveX.
Используем фреймворк, чтобы выполнить все эти вычисления где-нибудь в фоне, а результат вернуть обратно — в главный поток. Издатель выполнит у себя метод prepareList , а результат, объект класса UserList, уйдет подписчикам.
Многопоточность в ReactiveX создается просто: указываем, что вычисления нужно выполнять в отдельном потоке (subscribeOn), а результат вернуть в главный поток (observeOn).
Блок Model должен не только создать объект на основе класса HeavyOperation, но и реализовать get-метод, дающий доступ к нему извне.
Теперь перейдем к Presenter, связующему блоку. Здесь необходимо сохранить ссылки на блоки Model и View, а также связать их между собой get-методами.
Генерация данных будет происходить только по команде от View, для этого создан метод loadDatawithRx. Вполне возможно, действий будет больше, чем просто генерация списка. К примеру, имеет смысл сразу запустить на экране анимацию загрузки данных.
В Presenter нужно прошить из Model ссылку на объект издателя, для этих целей и были созданы get-методы.
Издатель уже есть, осталось сгенерировать подписчика. Создадим его тут же, используя метод subscribe и класс Observer.
И вот сила ReactiveX в действии — нам удалось связать паттерном «Наблюдатель» блоки Model и View. Метод doTheJob сгенерирует новый поток, создаст в нем список, а потом данные сразу же попадут во View через метод showResult. При этом приложение продолжает жить активной жизнью, без задержек и перерасхода памяти.
Кстати, мы еще совсем ничего не знаем о блоке View. По сути, код тут может быть совершенно произвольным, никак не завязанным на реактивном программировании. Все, что от View требуется, — реализовать методы inProgress и showResult, вызываемые из связующего блока.
Чтобы паттерн заработал, осталось только инициализировать объекты на основе созданных классов — сделать это можно где-нибудь в MainActivity.
Рис. 3. Многообразие портов ReactiveX
Лямбда-выражения
При работе с ReactiveX каждый раз приходится создавать новые объекты и тут же переопределять какие-то методы. В результате возникает нагромождение малоиспользуемых объектов и такой код плохо читается.
В Java 8 появились так называемые лямбда-выражения, которые очень похожи на функции в С-подобных языках. Не так давно они стали доступны и в Android (с обратной совместимостью API до версии 23), теперь можно сделать код более читаемым и лаконичным.
ReactiveX v.2
Outro
ReactiveX может выручить в ситуациях, которые раньше казались практически нерешаемыми. Попробуй написать что-нибудь самостоятельно, и ты поймешь, насколько это мощный инструмент. Как обычно, на нашем сайте будет весь исходный код созданного сегодня приложения. Если останутся вопросы, пиши мне на почту, разберемся вместе. Удачи!
Сердце Амстердама поистине красивое место с его возвышенной планомерной архитектурой и благоприятной инфраструктурой. Я по-настоящему наслаждаюсь, изучая этот город, особенно район Йордан. Тихое место с ощутимой душой художника и умиротворяющей атмосферой. Это потрясающий пример сложной реновации, потому что в 70-х годах это был типичный район рабочего класса.
«Анимирование Vue: способности и элегантность Vue.js с точки зрения анимации»
Сара Дреснер
Фреймворк VUE JS: быстрый старт
Получите бесплатный курс и узнайте, как создать веб-приложение на трендовой Frontend-технологии VUE JS с полного нуля
Vue предоставляет простые решения для создания сложных и красивых взаимодействий, которые в то же время безопасны для пользователей. Мне очень понравился слайд, после которого следует очень четкое объяснение того, что на самом деле есть реактивность, где она используется, а где нет:
Мишель Вестстрэйт
Итак, теперь мы знаем, что React не реактивный, потому что он декларативный, и название этого фреймворка сильно вводит в заблуждение. Слово «планировщик» подходит лучше, я думаю. Но это не ошибка, поскольку он не навязывает какую-либо парадигму, которую вы хотите использовать.
Как сказал Майкл, это пища для ума, и вы можете использовать её для чего-либо новаторского в мире программирования. Я слышал анекдот «если что-то еще не написано в React, то скоро будет», как и до недавнего времени шутили о правиле с JavaScript.
«MobX State Tree + React: чистая реактивность»
Лука Меццалира
Фреймворк VUE JS: быстрый старт, первые результаты
Получите бесплатный курс и узнайте, как создать веб-приложение на трендовой Frontend-технологии VUE JS с полного нуля
«Увлекательное будущее React»
Китце Ристовски
После наилучшего выпуска Fiber версии, число 16,4 приносит нам новые модели, такие как Portals и новые методы, такие как componentDidCatch, createRef, новый Context API, который возможно, обеспечит удобство и простоту использования без классов. Кроме того, в новых компонентах версии componentWillMount, componentWillUpdate, componentWillReceiveProps будут устаревшими, так что лучше не использовать их в своих будущих проектах.
Мой любимый слайд для фанатов Vue:
«Почему мне никто не сказал об ngrx/entity?»
Джерард Санс
Внутри ngrx 5 у нас есть новые операторы Pipeable select и RxJS 5.5 . Ngrx / entity является хорошим выбором для более эффективной более чёткой производительности управления списками.
«Настоящее и будущее Vue»
Эван Ю
Как и в Mobx 4.0 , Vue 2.6 Proxies будут реализованы на основе реактивной системы. В новой версии Vuex они упрощают API с помощью async/await, и потенциально могут исключить разделение действий и мутаций.
«Apollo, GraphQL и Vue: конечный стек»
Гийом Чау
Механизм Apollo обеспечивает нормализованный кэш и дает возможность использовать их наблюдательную систему (они используют реализацию zen-observable, чтобы использовать ее в качестве полифила наблюдаемого предложения ).
Как вы видите, Observables , вероятно, происходят из Javascript. Стоит отметить, что шаблон Observables является рабочей лошадкой реактивной парадигмы. Поэтому просто приготовьтесь использовать его для лучшей цели и помните о том, что мир вокруг вас работает с реактивностью, поэтому, может быть, и вам пришло время написать все более реактивно?
Сама конференция была отличной возможностью увидеть, что наиболее ценится в данных рамках. Если вас интересует предмет VueJS, я рекомендую вам посетить VueJS Amsterdam в следующем году. Если ваш ответ иной, подумайте о Frontend Love или и о том и о другом.
Спасибо организаторам и надеюсь увидеть вас в следующем году!
Автор: Aleksander Kućma
Редакция: Команда webformyself.
Фреймворк VUE JS: быстрый старт, первые результаты
Получите бесплатный курс и узнайте, как создать веб-приложение на трендовой Frontend-технологии VUE JS с полного нуля
Читайте также: