Привет.
В этой статье пойдет речь о том, из чего состоит Marionette.js, и о возможности не писать свой велосипед.
Статья рассчитана в первую очередь на работавших с Backbone.js и/или Marionette.js.
Для вновь знакомящихся будет полезна первая, обзорная, часть и ссылки в конце статьи.
- Underscore.js — более 80 функций-помощников для удобной, кросс-браузерной работы с JavaScript.
- Backbone.Events — реализация базового класса событий плюс функции-помощники для связывания (bind) событий с объектами.
- Backbone.Model — модель данных с доступом до атрибутов ключ/значение. Имеет механизм загрузки/синхронизации с сервером.
- Backbone.Collection — коллекции моделей, приправленные 28 методами из Underscore.js. Как и модели, они имеют механизм загрузки/синхронизации с сервером.
- Backbone.Router и Backbone.history — базовые роутер и история страницы. Умеют детектировать совпавший путь и производить навигацию.
- Backbone.sync — реализация загрузки/синхронизации данных через REST с помошью jQuery.ajax.
- Backbone.View — представление. Из коробки не умеет ни рендерить себя, ни вставлять в DOM дерево.
Часть первая: Marionette.js
Представим, что мы пишем приложение на Backbone.js. В итоге мы получаем:
- Базовое представление, которое реализует поддержку нужного нам шаблонизатора и нужные нам взаимосвязи с моделями;
- Штуку, которая рендерит представление и вставляет его в DOM дерево;
- Штуку, которая умеет отображать коллекции;
- Штуку, которая отвечает за жизненный цикл представлений;
- Расширение над роутером;
- Штуку, которая запускает наше приложение;
- Модульную систему, будь то папки с исходниками или одна из реализаций модулей JavaScript;
- Скорее всего, что-либо ещё.
Под термином «штука» могут подразумеваться различные архитектурные реализации необходимых функций.
На прошедшей конференции BackboneConf неоднократно звучала идея, что Backbone.js — это основа (скелет) для написания фрэймворка. Причем основа достаточно хорошая.
Marionette.js является одной из библиотек, которая, используя всю гибкость Backbone.js, создаёт набросок архитектуры и реализует основу для написания больших веб-приложений.
Подробнее о том, из чего состоит Marionette.js (с картинками):
Backbone
- Все, что нам предоставляет Backbone.js, мы можем использовать и в Marionette.js. Исключением являются представления Backbone.View, использовать их не имеет смысла, поскольку они не совместимы с механизмом Marionette.js добавления в DOM дерево.
Представления
- Marionette.View — базовый класс для всех представлений Marionette.js. Реализует функции работы с DOM элементами и их событиями. Не предназначен для прямого использования.
- Marionette.ItemView — представление для рендеринга данных на одном шаблоне. Данными могут выступать либо одна модель, либо одна коллекция. Если данные представлены коллекцией, то все её элементы будут переданы в шаблон массивом.
- Marionette.CollectionView — представление пробегает по всем элементам коллекции и для каждой рендерит ItemView. Этот вид представлений не имеет своего шаблона, каждая из ItemView добавляется к “el” контейнеру CollectionView.
- Marionette.CompositeView — тоже самое, что и CollectionView, плюс свой шаблон и модель данных для контейнера. Мы должны явно указать элемент-контейнер для добавления ItemView. Подходит, например, для отображения модели “условий поиска” и коллекции найденных записей.
- Marionette.Layout — представление, которое отображает произвольный шаблон с данными как ItemView, плюс создаёт менеджер регионов, о котором пойдет речь ниже.
Управление представлениями
- Backbone.BabySitter — контейнер для управления дочерними представлениями. Используется в CollectionView и CompositeView. Может быть использован c Backbone.View без Marionette.js.
- Marionette.Region — управление регионом (областью) на странице. Простыми словами, регион — это объект, который содержит один html элемент и умеет вставлять в него html содержимое других представлений. В один момент времени регион отображает только одно представление. При добавлении нового представления в регион удаляется старое html содержимое и закрывается сам объект представления.
- Marionette.RegionManager — управление группой регионов.
- Marionette.Renderer — реализация рендеринга html из шаблона и данных. Используется во всех представлениях Marionette.js.
- Marionette.TemplateCache — ленивая загрузка и кэш шаблонов underscore. Используется Marionette.Renderer. Для использования другого шаблонизатора рекомендуется переопределять именно Renderer и/или TemplateCache.
Модули и приложение
- Marionette.Module — модули не предназначены для отслеживания зависимостей и никак не связаны со сборкой приложения, в отличие от AMD/CommonJS модулей. Модуль Marionette.js выполняет следующие важные для клиентского JavaScript задачи: запуск и остановка логической части приложения, элегантное пространство имен с возможностью описания одного модуля в нескольких файлах.
- Marionette.Application — приложение можно описать как корневой модуль и базовый менеджер регионов. Имеет набор шин сообщений и механизм запуска всех модулей.
- Marionette.Controller — дословный перевод: объект общего назначения для управления модулями, маршрутизаторами и представлениями. Реализует паттерн медиатор (посредник). Подробнее о контроллере/медиаторе — ниже.
Шины сообщений
- Application.vent — глобальный экземпляр Backbone.Wreqr.EventAggregator. Реализует паттерн pub/sub. Подписчиков и публишеров может быть сколько угодно.
- Application.commands — глобальный экземпляр Backbone.Wreqr.Commands. Подписаться на исполнение определённой команды можно только один раз. Если команда отправлена до создания «исполнителя», она будет исполнена при его создании.
- Application.reqres — глобальный экземпляр Backbone.Wreqr.RequestResponse. Реализует паттерн request/response. Исполнять запрос может только один подписчик.
Остальное
- Marionette.AppRouter — роутеры в Marionette.js должны быть тонкими, не должно быть “глобального” роутера всего. Задача роутера сводится к вызову нужного метода контроллера при совпадении пути, никакой бизнес логики.
- Marionette.Callbacks — внутренний помощник вызова цепочки обратных вызовов.
Вывод: не пишем свой фрэймворк, используем Marionette.js.
Часть вторая: Контроллер/Медиатор
Настоятельно советую посмотреть все 9 часов скринкастов BackboneRails от Brian Mann. Материалы содержат огромное количество полезных штук, одна из которых является подробным описанием использования контроллера/медиатора.
Основная идея: жизненным циклом каждого представления или логически связанной группой представлений (Layout) управляет контроллер/медиатор.
Подробный пример: нам нужно отобразить список, пусть будет список «Яблок».
Мы создаём новый контроллер списка «Яблок». На контроллер, в свою очередь, ложатся задачи:
- Запросить необходимые данные (у провайдера данных через шину сообщений);
- Дожидаться загрузки данных, возможно, показывая спиннер загрузки;
- В случае списка «Яблок» отобразить CollectionView или CompositeView;
- Слушать события отображенного представления, например, клики по записи каждого яблока;
- При необходимости перенаправлять события в глобальную шину сообщений;
- При закрытии представления закрыть и самого себя;
- При закрытии самого себя закрыть и все свои подписки (bind) на события.
Подробнее о первом пункте
Логика того, “как” дожидаться данных, вынесенная из провайдера данных в контроллер/медиатор, позволяет нам в каждом конкретном случае обрабатывать это наиболее приемлемым для интерфейса пользователя способом. Например, в одном случае мы можем покрутить спиннером и после получения данных все отобразить, а в другом случае сначала отобразить пустое представление, а потом заполнить полученными данными.
Подробнее о пятом пункте
Казалось, можно было бы напрямую отправлять события представления в глобальную шину событий. Но прослойка в виде контроллера/медиатора позволяет нам использовать события представления в полной мере и проксировать события, необходимые только всему приложению. Пример: в случае яблок можно привести в пример кнопку “удалить яблоко” в этом списке и диалог подтверждения (“вы действительно хотите удалить это яблоко?”); только после двух этих событий, обработанных контроллером/медиатором, мы отправляем в глобальную шину сообщений, что такое яблоко удалено.
Вывод: контроллер/медиатор, реализующий паттерн медиатор (посредник), отлично подходит на роль связующего звена асинхронных данных, рендеринга представлений и действий пользователя.
Ссылки и материалы:
- Официальный сайт Mariontette.js
- Примеры кода на каждый класс Marionette.js
- Видеозаписи конференции BackboneConf 2013
- Скринкасты по Marionette.js от Brian Mann (Часть платных, своих денег стоят)
- Наиболее актуальная на настоящее время презентация по Marionette.js от Brian Mann
- Почему мы перешли на Marionette.js
- Сравнение Marionette.js и Chaplin.js
Автор: mahnunchik