...

понедельник, 30 декабря 2013 г.

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




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



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



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

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


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




This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers.


Комментариев нет:

Отправить комментарий