MVC-фреймворки на JavaScript: сравнение Marionette и Chaplin
Разработка JavaScript-приложений — горячая тема и люди интересуются какой же фреймворк лучше выбрать. В этой статье я сравню два из них.
Marionette и Chaplin — фреймворки, которые работают поверх популярной библиотеки Backbone.js. Оба хотят облегчить разработку одностраничных JS-приложений. В таких приложениях, фронтэнд выполняет задачи, которые в прошлом выполнялись на сервере (вроде рендеринга HTML из данных).
Бэкбон спроектирован, как минималистичная библиотека, а не как полноценный фреймворк. Мой опыт показал, что Бэкбон хорош только как ядро архитектуры JS-аппликейшна. И Марионетка, и Чаплин появились, потому что Бэкбон предоставляет мало структурирования для реальных приложений. Они решают те же проблемы. Так что между ними довольно много сходств — возможно, даже больше, чем отличий.
Сперва-наперво хочу заявить, что я со-автор Чаплина. Но я так же работал с Марионеткой и слежу за ее разработкой. Еще есть другой амбициозный фреймворк на базе Бэкбона — Торакс. Так как я не работал с ним, я не включил его в это сравнение.
Содержание
- Нетехнические аспекты
- Общие особенности, устраняющие недостатки Бэкбона
- Достоинства Марионетки
- Недостатки Марионетки
- Достоинства Чаплина
- Недостатки Чаплина
- Заключение
Нетехнические аспекты
Я буду говорить о технических деталях позже, но, давайте посмотрим правде в глаза — на решение о выборе той или иной программы сильно влияет их маркетинг, репутация, истории успеха и документация.
Марионетка и Чаплин — проекты с открытыми исходниками, выпускаемые под MIT-лицензией и активно разрабатывающиеся на Гитхабе. Их авторы участвовали в разработке нескольких больших Бэкбон-приложений и перенесли свой опыт создания хорошей архитектуры, чтобы вам не пришлось наступать на те же грабли.
Хорошо известные компании используют Марионетку и Чаплин. Сложно сказать точно, но в целом количество пользователей примерно одинаково. Экосистема Марионетки больше, поэтому много людей используют только какие-то части Марионетки вместо всей либы.
Чаплин был более популярен в начале, Марионетка же известной стала позже. Марионетка более дружелюбна к новичкам и хорошо задокументирована, что, видимо, является одной из причин, по которым ее выбирают. Еще одна из причин популярности — вклад Дэрика Бэйли, создателя Марионетки, который написал кучу статей о разработке Бэкбон-аппликух. Он так же выступает на конференциях и записывает видеоуроки.
Общие особенности, устраняющие недостатки Бэкбона
Событийно-ориентированные архитектуры без бардака
Ключевая особенность Бэкбона — разделение ответственности между вьюшками и моделями. Они взаимодействуют через события (Publish / Subscribe). Используя Backbone.Events
, можно сделать событийно-ориентированную архитектуру. Это хороший способ отделить части вашего приложения друг от друга.
И Марионетка, и Чаплин определяют узкие места приложений на Бэкбоне. Очистка event listeners критична в событийно-ориентированной архитектуре. Жизненный цикл важен: компонент, создавший другой компонент, отвечает за его удаление. Марионетка и Чаплин решают эти проблемы по-разному. Они не только пропагандируют использование Publish / Subscribe, но так же дают инструменты для избежания частых ошибок, связанных с подходом.
Структурирование приложения
Модели и вьюшки — низкоуровневые паттерны. Поверх этого, Бэкбон предоставляет только Роутеры. Это очень тонкий слой и, наверное, самая проблемная часть Бэкбона. С чисто бэкбоновским роутером нельзя создать нормальную высокоуровневую архитектуру с менеджментом памяти. И Марионетка, и Чаплин вводят контроллеры.
Мощные соглашения вьюшек
Следуя простоте Бэкбона, его вьюшки и их рендеринг — абстрактная штукенция. Бэкбоновская вьюха отвечает за один DOM-элемент, но Бэкбон не предоставляет инструментов из коробки для забивки данных в элемент и вставки его в страницу — метод View#render
по-умолчанию пуст.
Марионетка и Чаплин предоставляют классы вьюшек со здравым механизмом рендеринга (см. Marionette.ItemView и Chaplin.View). Поверх этого, вам нужно выбрать какой-нибудь язык шаблонизации, типа Mustache / Hogan, Handlebars или HAML-coffee.
Обе библиотеки имеют соглашения о том, когда рендерить вьюшки и как вставлять их в DOM. Вы можете трансформировать данные модели перед тем, как они будут отправлены в шаблон. Это полезно, например, для вычисляемых свойств (computed properties).
Вьюшки, наверное, самая сложная часть вашего приложения, поэтому Марионетка и Чаплин дополнительно предоставляют хелперы. С ними можно создавать вложенные вьюшки и декларировать именованные регионы. Еще можно декларативно регистрировать события моделек (легче и читабельней, чем this.listenTo(this.model, ...);
).
Чистый Бэкбон это еще и минус вьюхи для работы с коллекциями (см. Marionette.CompositeView и Chaplin.CollectionView). Суть такова: использовать по отдельной вьюшке для каждой модели коллекции. С этим сложные списки могут быть реализованы через простой и структурированный код. Вьюхи для коллекций сами по себе умные, они не будут перегенерировать весь список из-за одной модельки.
Достоинства Марионетки
Марионетка — кладезь полезных паттернов. Она модульна, от вас не требуется использовать все части Марионетки. Можно начать с нескольких частей и заюзать остальные позже. Часть возможностей Марионетки идут от отдельных плагинов для Бэкбона, например, Backbone.BabySitter и Backbone.Wreqr.
В Марионетке так же есть клевые уникальные фичи. По-моему, самые мощные — модули приложения и умное управление вьюшками.
Модули
Модули приложения — независимые части архитектуры, которые могут состоять из роутеров, контроллеров, моделей и вьюшек. Модули могут быть запущены и остановлены, еще можно определить “хуки”, которые будут выполнены перед остановкой. Они так же могут быть лениво загружены, когда роут совпадает с выставленным в модуле.
BBCloneMail — пример приложения, которое состоит из двух модулей (почта и контакты). В этом примере, только один модуль активен в один период времени. Но в общем, модули могут и не “заменять” друг друга. Модули имеют свои роутеры, которые должны быть запущены с самого начала (роутер контактов, роутер почты).
Модули могут быть вложены друг в друга. Ваша главное приложение, Marionette.Application так же является модулем. Технически, есть некоторые различия между Marionette.Application и Marionette.Module, но, я надеюсь, в будущем они станут более однообразны.
Вам, скорее всего, не понадобится пользовать несколько модулей с самого начала, но это — хорошая фича, которая помогает разделить приложение на мелкие, связанные единицы.
Управление вьюшками
Другая мощная часть Марионетки — умное управление вьюшками. Вьюшки могут быть легко вложены друг в друга, используя BabySitter. Вдобавок, Марионетка добавляет несколько абстракций, как Layouts и Regions. Layout — вьюшка, которая содержит именованные регионы. Region — объект, который отвечает за DOM-элемент. Примерами регионов являются header, navigation, main, sidebar и footer.
Где нужно рендерить вьюшки и вставлять их в DOM? В регионах! Вместо управления DOM-элементами напрямую, можно определить Регион один раз и далее просто писать mainRegion.show(view)
, например. Это отрендерит вьюшку и присобачит ее в DOM-элемент, отвечающий региону. Регион держит только одну вьюшку в один период времени, поэтому старая вьюшка “закрывается” (удаляется из DOM и безопасно высвобождается из памяти).
С вложенными регионами можно построить сложный интерфейс просто, через читабельный и поддерживаемый код.
Недостатки Марионетки
Для краткости, я описал пару уникальных возможностей Марионетки.
Мне не нравятся слабые абстракции и невнятные рекомендации по эффективному использованию.
Роутинг и контроллеры
Например, Марионетка предоставляет очень мало надстроек поверх Backbone.Router. По-моему, это важно, так как бэкбоновский роутер не предоставляет никаких соглашений об очистке созданных объектов из памяти, когда следующий роут становится активен. В принципе, можно реализовать централизованную чистку, используя события route
, но это костыль.
В Марионетке есть модули, которые могут быть остановлены и регионы, которые могут быть закрыты. Но каждый раз стартовать и останавливать модули или закрывать регионы явно не комильфо.
Marionette.AppRouter — шаг в нужном направлении. Его идея в отделении конфигурации роута от кода. При совпадении роутов, AppRouter обращается к отвечающему ему экземпляру Контроллера.
Контроллеры в Марионетке не имеют определенного предназначения, они лишь “контролируют” что-то. Они могут подписываться на события через Backbone.Events
, у них есть методы initialize
и close
. Это определенно полезно, но нужно самому думать, как их использовать и когда. Обычно, там создаются модели и вьюхи.
Глобальные и приватные объекты
В Марионетке, модули и классы сидят в глобальной переменной, например, window.BBCloneMail.MailApp.Controller
. С марионеткой становится заманчиво создавать глобально доступные экземпляры классов, хотя делать так совсем не круто. В примере BBCloneMail, некоторые объекты передаются и возвращаются из функций, в то время, как другие доступны глобально (например, BBCloneMail.MailApp.controller).
Когда читаешь код, не очевидно, какие объекты глобально доступны и какие глобально используются. С Марионеткой, я советую использовать паттерн object-capability, с которым можно связывать объекты без глобальных неймспейсов.
Шаблоны по-умолчанию
По-умолчанию, вьюшки читают их шаблоны из DOM и компилируют их андерскоровским языком шаблонов (_.template). Это просто, но встраивание шаблонов в HTML не рекомендуется (например, гайдлайнами Chrome Web Store — приложения с компилируемыми на лету шаблонами туда просто не пустят). В конце концов, шаблоны должны находиться в отдельных файлах, прекомпилироваться. Но, конечно, вы можете легко добавить это в Марионетку — достаточно поработать с Renderer.
Достоинства Чаплина
В сравнении с Марионеткой, Чаплин больше похож на фреймворк. Он более упрям и имеет более сильные соглашения в разных областях. Он взял идеи серверных MVC-фреймворков типа Ruby on Rails, которые следуют принципу convention over configuration. Цель Чаплина — предоставить доказавшие себя best practices и удобную среду для разработки.
Кофескрипт и ООП
Чаплин написан на Кофескрипте, мета-языке, компилирующемся в JavaScript. Однако, приложения на Чаплине могут и не использовать кофе, ведь Чаплин — всего-навсего очередная JS-библиотека.
Использование кофескрипта — часть чаплиновской идеи о том, что разработка должна быть простой. Кофескрипт навязывает гайдлайны Дугласа Крокфорда из книги “JavaScript — The Good Parts”. Как Марионетка, Чаплин пропагандирует использование ECMAScript 5 Strict Mode.
С кофескриптом, определять и наследовать классы проще и чище, чем через Backbone.extend
.
Еще Чаплин активно пропагандирует переопределение методов в дочерних классах и использование кофескриптового super
(или его аналога в JavaScript) и пытается заставить классовое наследование работать четко. Например, если вы декларативно определите события у класса и его наследника (через View#events
) — и те, и другие будут применены.
Модульность через AMD или CommonJS
Чаплин требует использовать модули — сойдут CommonJS или AMD. Каждый модуль должен определять свои зависимости и экспортировать что-нибудь (функцию-конструктор или одиночный объект). Обычно в Чаплине на один файл приходится один класс и один модуль.
Использовать AMD не просто, нужно знать загрузчики а-ля require.js и оптимизаторы типа r.js. Как альтернатива, можно использовать модули CommonJS и сборщик Brunch, там все сильно проще.
Марионетка так же поддерживает AMD, однако не принуждает к их использованию.
Фиксированная структура приложения
Чаплин дает фиксированную структуру. Она обрабатывает основную часть всех действий.
Application
является корневым классом, который запускает эти частиRouter
заменяетBackbone.Router
Dispatcher
стартует и останавливает контроллеры при совпадении роутаLayout
является главной вьюшкой, которая следит за кликами на ссылках
В Чаплине, есть центральное место для определения всех роутов. Роут указывает на действие контроллера. Например, шаблон ссылки cars/:id
указывает на cars#show
, то есть на действие (метод) show
контроллера CarsController
.
Контроллер — это место, где инициализируются модели и вьюшки (для главной части экрана, а не сайдбаров-хедеров). В целом, действие контроллера обычно отвечает одному экрану вашего приложения.
Управление памятью
Главная идея Чаплина — вычищаемые из памяти контроллеры. Главное правило — текущий контроллер и все его свойства (модели, коллекции, вьюшки) удаляются на следующем контроллере. Даже если роут указывает на другое действие одного и того же контроллера, старый экземпляр контроллера удаляется и новый создается.
Удаление предыдущих объектов при совпадении следующего роута — простой и эффективный способ очистки ссылок. Но некоторые объекты лучше бы иметь в памяти на протяжении нескольких или всех действий. Для этой цели имеется Chaplin.Composer, который позволяет расшаривать модели и вьюшки. Если сохраненный объект не используется следующим роутом, он автоматически очищается.
В приложениях Чаплина, каждый объект легко подчищается. Все классы Чаплина имеют метод dispose
, который сделает объект непригодным к использованию, удалит все связи и заморозит его.
Скрытые экземпляры классов и Publish / Subscribe
Хорошо известное правило программирования на JavaScript — избегание глобальных переменных, и Чаплин пытается принудить не юзать их. Классы в Чаплине — CommonJS / AMD модули, которые недоступны в глобальном неймспейсе. Все экземпляры, в свою очередь, скрыты. Два экземпляра не должны иметь ссылки друг на друга, если только они не тесно связаны, как вьюшка и ее модель.
Объекты могут общаться друг с другом в несвязанной форме, используя паттерн Publish / Subscribe. Для этой цели, есть Chaplin.Mediator. Медиатор так же может быть использован для хранения некоторых частоиспользуемых вещей, типа модельки текущего пользователя. После создания нужных свойств, медиатор запечатывается, дабы не стать глобальной помойкой вашего приложения.
Управление вюшками
Чаплин так же силен в управлении вьюшками. В нем есть именованные регионы и вложенные вьюхи. Чаплин использует иной подход к рендерингу вьюшек и их вставке в DOM. У вьюшек может присутствовать флаг autoRender
и опция container
. Если они есть, вьюшки будут рендериться сразу после создания и вставляться в DOM автоматически. Вместо опции container
, вы можете указать region
, чтобы вместо прямой ссылки на DOM-элемент, присоединить вьюшку в ранее зарегистрированный регион.
Помимо глобальных регионов, в Чаплине нет абстракций Марионетки типа Marionette.Layout
и Marionette.Region
. В приложении с Марионеткой обычно присутствуют несколько вложенных регионов и лайаутов. В приложении с Чаплином же у вас будет меньше главных регионов и больше вложенных в них вьюшек. Само собой, еще можно создавать вьюшки для многоразового использования с поведением Marionette.Layout
, например, ThreePaneView
.
Недостатки Чаплина
Как со-автор Чаплина, я могу быть необъективен. Но я так же вижу слабости и потенциал к улучшению. Очевидно, что Марионетка нашла более подходящие решения к некоторым проблемам.
Как я уже заметил, Чаплин очерчивает жизненный цикл каждого компонента и поэтому силен в управлении памятью. Что является одной из основных проблем при создании веб-аппликух на Бэкбоне. Чаплин нашел решение, которое хорошо работает, но оно не идеально и спорно. Эта особенность развивалась и должна развиваться дальше.
Новичкам сложно понять всю картину Чаплина сразу. Управление памятью, модульность и другие концепции Чаплина новы для многих JavaScript-разработчиков. В то же время, если в начале эти фичи кажутся “в тягость”, приложение выиграет от них в долгосрочной перспективе.
Publish / Subscribe не уникальная возможность Чаплина, если сравнить с app vent Марионетки. На самом деле, app vent более гибок, так как каждый модуль идет с собственным аггрегатором событий.
Чаплин использует Pub / Sub не только для вещания событий, а и для запуска команд, что есть злоупотребление пабсабом. Backbone.Wreqr реализовывает паттерн Command / Request / Response для этого случая. Чаплин должен перенять эту возможность у Марионетки.
Заключение
Марионетка более модульна, можно использовать лишь части, которые нравятся (я считаю, что вы должны использовать большинство из них, так как они могут улучшить приложение). Вместо одной центральной структуры, вы можете создать составную архитектуру с независимыми модулями. Это предоставляет большее разделение компонентов, но вы должны понять, как их правильно использовать.
Чаплин более похож на фреймворк, он централизован и строг. Авторы Чаплина считают, что соглашения повышают удобство и продуктивность. Но, конечно, ваше мнение на этот счет может быть другим.
Из-за его целей, Чаплин охватывает большее количество проблем, чем другие библиотеки. Например, имеются богатые на возможности роутинг и система диспетчеризации, заменяющие Backbone.Router, но использующие Backbone.History.
В сравнении с Марионеткой, Чаплин монолитен. Это не значит, что вы не можете делать вещи по-другому. Вы все еще можете настраивать, модифицировать или менять базовые классы и нарушать все правила.
Стоя на плечах гигантов
Так какую же библиотеку выбрать? Я не думаю, что выбор однозначен. Очевидно, вы должны строить архитектуру на той библиотеке, которая больше подходит под ваши нужды. Но вы так же должны понять и научиться применять ее best practices.
Используя Бэкбон, вам нужно разрабатывать масштабируемую архитектуру приложения самому. Не пишите приложения на чистом Бэкбоне, наступая на те же грабли, что и другие, перенимайте их опыт. Пройдитесь по Марионетке, Тораксу, Ауре, Чаплину и другим архитектурам, чтобы изучить их.
Чтобы начать использовать Чаплин, я рекомендую использовать один из наших базовых бойлерплейтов: CoffeeScript boilerplate с поддержкой шаблонов Handlebars, или его аналог на чистом JavaScript. Они включают в себя некоторые соглашения, которые мы находим весьма полезными: структура папок и соглашения именования файлов, стиль написания кода, движки шаблонов.
Если вы ищете зрелую среду разработки для быстрого старта, попробуйте
Brunch with Chaplin или Chaplin + Rails boilerplate.
Для более практического введения в Марионетку, читайте статью на Smashing Magazine (первая, вторая части). На вики Марионетки находится целая куча статей, видеоуроков и презентаций.
Благодарности
Спасибо Дэрику Бэйли, Себастьяну Дойчу, Полу Миллеру и Полу Виттману за их рецензию этой статьи и вклады в разработку Марионетки и Чаплина.
ссылка на оригинал статьи http://habrahabr.ru/post/177843/
Добавить комментарий