1 Введение
В статье мы сравним три популярных MV* фреймворка для веб-разработки: AngularJS, Backbone и Ember. Выбор подходящего фреймворка для проекта кардинально влияет на вашу возможность выполнять задачи вовремя и поддерживать ваш код в будущем. Вам нужен надёжный, проверенный фреймворк, но вы не хотите, чтобы он вас ограничивал. Веб быстро развивается, и старые методики уходят в прошлое. Займёмся же подробным их сравнением.
2 Познакомьтесь с фреймворками
У всех рассматриваемых фреймворков есть общие черты: их код открыт, выпущен под лицензией MIT, и они решают задачу создания одностраничного приложения через шаблон проектирования MV*. У всех есть концепции видов, событий, моделей данных и путей.
AngularJS родился в 2009 как часть большего коммерческого продукта GetAngular. Вскоре после этого Миско Хевери, один из инженеров-основателей GetAngular, сумел воссоздать при помощи этого продукта веб-приложение, состоявшее из 17 тысяч строк кода и делавшееся в течение 6 месяцев, всего за 3 недели, и уложиться при этом в тысячу строк кода. В Google впечатлились таким фактом и стали спонсировать проект с открытым кодом AngularJS. Среди его возможностей – двустороннее связывание данных, инъекции зависимостей, простой для тестирования код и расширение возможностей HTML при помощи директив.
Backbone.js – легковесный MVC-фреймворк, родившийся в 2010. Популярность набрал в качестве альтернативы тяжёлым фреймворкам вроде ExtJS.
Ember родом из 2007 года. Он начинался как SproutCore MVC фреймворк, и сначала его разрабатывали SproutIt, затем – Apple. В 2011 году его форкнул Иехуда Кац, один из главных программистов в проектах jQuery и Ruby on Rails.
3 Сообщества
Сообщество – один из важных факторов для выбора фреймворка. Больше сообщества – больше ответов на вопросы, сторонних модулей, тьюториалов на YouTube… Я составил табличку, актуальную на апрель 2014 года. Angular явно выигрывает – это 6-й популярный проект на GitHub и вопросов по нему на StackOverflow больше, чем в сумме у Ember и Backbone:
Кроме текущих показателей можно при помощи Google Trends посмотреть на скорость роста популярности фреймворков:
4 Размеры фреймворка
Время загрузки страниц – вещь для успеха сайта критичная. Пользователи не отличаются терпением, поэтому необходимо стараться ускорять загрузку как можно сильнее. На это влияют два фактора – размер фреймворка и время, которое требуется ему на запуск.
Мы будем сравнивать минифицированные версии в архиве gzip. Но недостаточно сравнить размеры самих фреймворков. Например, Backbone.js, несмотря на малый размер, требует ещё Underscore.js (5kb) и jQuery (32kb) или Zepto (9.1kb).
5 Шаблоны
Angular и Ember включают движок шаблонов. А Backbone оставляет его на усмотрение разработчика. Лучший способ протестировать шаблоны – это взять пример кода. Мы возьмём пример форматирования списка в HTML.
5.1 AngularJS
У Angular шаблоны – это HTML со связывающими выражениями. Выражения окружены двойными фигурными скобками:
<ul>
<li ng-repeat="framework in frameworks" title="{{framework.description}}">
{{framework.name}}
</li>
</ul>
5.2 Backbone.js
Хотя Backbone можно интегрировать с несколькими движками шаблонов, по умолчанию используется Underscore. Но обработка шаблонов с его помощью довольно примитивна, и приходится добавлять код на JS:
<ul>
<% _.each(frameworks, function(framework) { %>
<li title="<%- framework.description %>">
<%- framework.name %>
</li>
<% }); %>
</ul>
5.3 Ember.js
Ember на сегодняшний день использует Handlebars – это расширение популярного движка Mustache. Сейчас разрабатывается новый вариант Handlebars по имени HTMLBars. Handlebars не понимает DOM – он просто работает со строками. HTMLBars будет понимать DOM.
<ul>
{{#each frameworks}}
<li {{bind-attr title=description}}>
{{name}}
</li>
{{/each}}
</ul>
6 AngularJS
6.1 Преимущества
Angular привнёс много новых концепций. Двустороннее связывание данных позволяет минимизировать код. Возьмём такой пример на jQuery:
$('#greet-form input.user-name').on('value', function() {
$('#greet-form div.user-name').text('Hello ' + this.val() + '!');
});
Благодаря двустороннему связыванию, такой код писать не придётся. Вы просто объявляете связку в шаблоне:
<input ng-model="user.name" type="text" />
Салют, {{user.name}}!
Обещания играют важную роль в angular. JS – язык однопоточный и событийный, поэтому много чего происходит в асинхронном режиме. Асинхронный код на JS быстро разрастается и превращается в спагетти из вложенных обратных вызовов. Его часто обзывают «Пирамидальный код» или «Ад с обратными вызовами».
У Angular не только самое большое сообщество, его поддерживает и рекламирует Google. Открытый код позволяет всем принимать участие в разработке фреймворка. Вот здесь находятся документы по дизайну Angular 2.0, и все могут знакомиться и комментировать их.
Angular разбивает приложение на блоки нескольких типов: контроллеры, директивы, фабрики, фильтры, сервисы и виды. Они, в свою очередь, разбиваются на модули. У всех блоков – своя роль. Виды занимаются UI, контроллеры – логикой интерфейса, сервисы общаются с бэкендом, директивы позволяют создавать компоненты и расширяют HTML.
«Angular написан с учётом тестируемости» – эта цитата из руководства по юнит-тестам говорит о многом. Действительно, Angular сильно заботится о разделении сущностей, изолировании юнитов и предоставляет мощные средства для работы со встроенными сервисами вроде $http и $timeout.
6.2 Недостатки
Angular часто критикуют за сложность API директив. Особенно запутывает разработчиков концепция трансклюзии, а кроме того есть такие вещи, как компиляция функций, пред- и пост- линкуемые функции, разные виды областей видимости и настройки директив.
Иерархия областей видимости использует прототипное наследование, которое сложно даётся для людей с опытом в Java и C#.
Angular Expressions широко используются в Видах. Этот язык довольно мощный, иногда даже чересчур. Он позволяет создавать сложную логику и проводить операции присваивания и различные подсчёты внутри шаблонов. Размещение логики в шаблонах усложняет тестирование. Рассмотрим следующий пример перегруженного вычислениями шаблона:
<button ng-click="(oldPassword && checkComplexity(newPassword) && oldPassword != newPassword) ? (changePassword(oldPassword, newPassword) && (oldPassword=(newPassword=''))) : (errorMessage='Введите пароль, соответствующий следующим требованиям: ' + passwordRequirements)">Жмите</button>
Во многих случаях легко ошибиться в имени директивы или функции, и тяжело найти эту ошибку.
Digest Cycle, обеспечивающий магию «грязных проверок» также часто удивляет разработчиков. Легко забыть вызвать $digest(), работая в контексте, отличном от Angular. Нужно заботиться о том, чтобы случайно не сделать бесконечные digest-циклы. На страницах с обилием интерактива Angular начинает тормозить. Не стоит использовать более 2000 связок на одной странице.
7 Backbone.js
7.1 Преимущества
Backbone – легковесный и не занимает много памяти. Кривая обучения линейная, и у него есть очень мало концепций для понимания (модели/коллекции, виды, пути). Отличная документация, а код простой. Вы даже можете пройтись по всему коду фреймворка, который хорошо документирован, и ознакомиться с ним в течение часа.
На его основе можно строить свои фреймворки. Примеры готовых: Backbone UI, Chaplin, Geppetto, Marionette, LayoutManager, Thorax, Vertebrae. В случае Angular и Ember вам приходится жить с тем, что для вас приготовили разработчики. В Angular 2.0 обещают это исправить, но до этого ещё надо дожить.
7.2 Недостатки
Backbone не предоставляет структуры. Это всего лишь набор простых инструментов для создания структуры, и вам нужно заполнить много пустых мест. Конечно, многие из этих мест заполняются сторонними плагинами – но это значит, что вам нужно принять много решений при их выборе. К примеру, вложенные модели можно сделать при помощи Backbone.DocumentModel, BackBone.NestedTypes, Backbone.Schema, Backbone-Nested, backbone-nestify и так далее. Выбор лучшего решения занимает время – а суть фреймворка в экономии вашего времени.
Нет поддержки двусторонней связанности данных, то есть вам придётся писать много вспомогательного кода для обновления вида при изменениях модели, и для обновления модели при изменениях вида.
Виды в Backbone напрямую манипулируют DOM, поэтому их сложно тестировать и сложнее повторно использовать.
8 Ember.js
8.1 Преимущества
Ember.js работает по принципу «соглашения вместо настроек». Ember не требует написания вспомогательного кода, он может сам догадаться до многих вещей, например автоматического определения имени пути и контроллера при определении ресурса. Он даже умеет автоматически создавать контроллер для ресурса, если вы его не определите.
Он включает хороший обработчик путей и опциональный слой для работы с данными под названием ember data. В отличие от двух других фреймворков, у Ember сразу есть модуль для работы с данными, который хорошо интегрируется с бэкендом Ruby-on-Rails или любым API с RESTful JSON.
При разработке Ember внимание уделялось быстродействию. Ваше приложение скорее всего будет грузиться и работать быстрее.
8.2 Недостатки
API бурно развивался, поэтому содержит устаревший контент и примеры, которые уже не работают. Взгляните на Ember Data Changelog, и вы поймёте, что я имею в виду. Многие ответы на вопросы на StackOverflow уже неактуальны.
Handlebars загрязняют DOM множеством тегов
, что не только усложняет HTML, но и может поломать CSS или интеграцию с другими фреймворками вроде jQuery UI Sortable.
9 Итоги
Мы рассмотрели преимущества и недостатки фреймворков. Целостный подход Ember к реализации MVC понравится тем разработчикам, которые исповедовали его в Ruby, Python, Java, C# и других ООП-языках. Он также подходит для создания быстро работающих приложений и избавляет вас от написания лишнего кода.
Backbone выступает за минимализм. Он мелкий, быстрый и простой в обучении, и обеспечивает вам минимум необходимых инструментов для работы.
Angular инновационно подходит к расширению возможностей HTML, он понравится веб-разработчикам. У него большое сообщество и поддержка со стороны Google, он будет расти и развиваться, и он хорошо подходит как для быстрого прототипирования, так и для объёмных проектов.