От Backbone.js к Marionette.js

в 9:07, , рубрики: Без рубрики

Привет.

В этой статье пойдет речь о том, из чего состоит 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 — представление для рендеринга данных на одном шаблоне. Данными могут выступать либо одна модель, либо одна коллекция. Если данные представлены коллекцией, то все её элементы будут переданы в шаблон массивом.
    От Backbone.js к Marionette.js
  • Marionette.CollectionView — представление пробегает по всем элементам коллекции и для каждой рендерит ItemView. Этот вид представлений не имеет своего шаблона, каждая из ItemView добавляется к “el” контейнеру CollectionView.
    От Backbone.js к Marionette.js
  • Marionette.CompositeView — тоже самое, что и CollectionView, плюс свой шаблон и модель данных для контейнера. Мы должны явно указать элемент-контейнер для добавления ItemView. Подходит, например, для отображения модели “условий поиска” и коллекции найденных записей.
    От Backbone.js к Marionette.js
  • Marionette.Layout — представление, которое отображает произвольный шаблон с данными как ItemView, плюс создаёт менеджер регионов, о котором пойдет речь ниже.
    От Backbone.js к Marionette.js

Управление представлениями

  • 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) на события.

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

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

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

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

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

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

Автор: mahnunchik

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js