Привет.
В этой статье пойдет речь о том, из чего состоит 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