От Backbone.js к Marionette.js

    Привет, Хабр.

    В этой статье пойдет речь о том, из чего состоит Marionette.js, и о возможности не писать свой велосипед.

    Статья рассчитана в первую очередь на работавших с Backbone.js и/или Marionette.js.
    Для вновь знакомящихся будет полезна первая, обзорная, часть и ссылки в конце статьи.

    Для начала вспомним, что такое и из чего состоит Backbone.js
    1. Underscore.js — более 80 функций-помощников для удобной, кросс-браузерной работы с JavaScript.
    2. Backbone.Events — реализация базового класса событий плюс функции-помощники для связывания (bind) событий с объектами.
    3. Backbone.Model — модель данных с доступом до атрибутов ключ/значение. Имеет механизм загрузки/синхронизации с сервером.
    4. Backbone.Collection — коллекции моделей, приправленные 28 методами из Underscore.js. Как и модели, они имеют механизм загрузки/синхронизации с сервером.
    5. Backbone.Router и Backbone.history — базовые роутер и история страницы. Умеют детектировать совпавший путь и производить навигацию.
    6. Backbone.sync — реализация загрузки/синхронизации данных через REST с помошью jQuery.ajax.
    7. 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) управляет контроллер/медиатор.
    Подробный пример: нам нужно отобразить список, пусть будет список «Яблок».

    Мы создаём новый контроллер списка «Яблок». На контроллер, в свою очередь, ложатся задачи:

    1. Запросить необходимые данные (у провайдера данных через шину сообщений);
    2. Дожидаться загрузки данных, возможно, показывая спиннер загрузки;
    3. В случае списка «Яблок» отобразить CollectionView или CompositeView;
    4. Слушать события отображенного представления, например, клики по записи каждого яблока;
    5. При необходимости перенаправлять события в глобальную шину сообщений;
    6. При закрытии представления закрыть и самого себя;
    7. При закрытии самого себя закрыть и все свои подписки (bind) на события.

    Подробнее о первом пункте

    Логика того, “как” дожидаться данных, вынесенная из провайдера данных в контроллер/медиатор, позволяет нам в каждом конкретном случае обрабатывать это наиболее приемлемым для интерфейса пользователя способом. Например, в одном случае мы можем покрутить спиннером и после получения данных все отобразить, а в другом случае сначала отобразить пустое представление, а потом заполнить полученными данными.

    Подробнее о пятом пункте

    Казалось, можно было бы напрямую отправлять события представления в глобальную шину событий. Но прослойка в виде контроллера/медиатора позволяет нам использовать события представления в полной мере и проксировать события, необходимые только всему приложению. Пример: в случае яблок можно привести в пример кнопку “удалить яблоко” в этом списке и диалог подтверждения (“вы действительно хотите удалить это яблоко?”); только после двух этих событий, обработанных контроллером/медиатором, мы отправляем в глобальную шину сообщений, что такое яблоко удалено.

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

    Ссылки и материалы:


    Друзья, про что из Marionette.js написать следующую статью?
    • 65%Модули Marionette.js89
    • 57%Модуль как контейнер для «действия» из CRUD78
    • 53%Сборка приложения на Marionette.js, AMD/CommonJS vs “cat *.js > app.js”73
    • 55%Варианты использования шин событий: Commands, EventAggregator, RequestResponse75
    • 47%Вольный перевод нескольких статей от Derick Bailey на тему использования роутеров65

    Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

    Метки:
    Поделиться публикацией
    Похожие публикации
    Комментарии 11
    • 0
      Можно все пункты по порядку описать. Каждый из них в равной степени будет интересен.
      • 0
        Marionette — хороший фреймворк, но какие могут быть аргументы в его пользу, когда есть такие решения как AngularJS или EmberJS?
        • 0
          Я вообще не спец в фронтенде, но читал что тот-же EmberJS сильно навязывает стиль. Шаг влево, шаг вправо и начинается борьба с самим EmberJS.

          Angular хочу попробовать, да.
          • +3
            Ваш вопрос можно два раза повторить, просто поменяв MarionetteJS, AngularJS и EmberJS местами.
            Это просто еще один инструмент, в чем то он лучше, в чем то хуже, в чем то просто другой. Сравнений фреймворков в сети уже предостаточно, повторяться было бы излишне.
            • +1
              Полностью с вами согласен, а если и делать ещё одно сравнение, то не комментарием, а отдельным постом.

              И в тему библиотек с «встроенным» двунаправленным дата биндингом, недавно наткнулся на плагин для Backbone.js от New York Times для добавления этих возможностей там где это необходимо https://github.com/NYTimes/backbone.stickit
            • 0
              Хотел бы согласиться, сам не устаю повторять, что «Right Tool For The Right Job», но как всегда есть одно большое мохнатое «но».

              Много используя и то, и другое, и третье — все-таки, Backbone (с Marionette/Thorax/Chaplin) — старая школа, Ember — чертовски хорош своими соглашениями и достаточно похож на Backbone концептуально, а вот Angular — хотя и добавляет необходимость «ментального сдвига» — позволяет решать большинство тех же задач, используя втрое-вчетверо-всемеро меньшее количество кода.
          • 0
            не работает ссылка

            Примеры кода на каждый класс Marionette.js shustoff.su/blog/javascript/backbone-marionette

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