5. Backbone Sucks

Jul 14, 2015 01:37

Кому-то могло показаться после прочтении предыдущей статьи, что мне "просто нравится бэкбон", или что я считаю его лучшей в мире технологией. Это не так. Мы начали с backbone по двум причинам: (1) многие с него начинают, и (2) это, пожалуй, минимально возможный фреймворк для браузерных приложений.

Так как мы приверженцы "бритвы Оккама" - сравниваться мы будем именно с ним, это как точка ноля. Теперь мы разберемся, с какими именно сложностями сталкиваются люди, использующие Backbone, и умеющие готовить его хорошо. Эти сложности однозначно надо исправлять, и по этим причинам можно выбрать другой фреймворк. А вот то, что в минимально возможном Backbone сложностей не вызывает - для этого нужна очень хорошая аргументация, чтобы это исправлять.

Вообще-то это черновик, написанный в один проход. Но мне сегодня влом вычитывать.


Из того, что мы можем хорошего сказать про Backbone - он быстр. Вот такой простой шаблон, сговняканый на коленке при помощи underscore.js, будучи развернут 100 раз в один и тот же узел DOM при помощи присваивания в innerHTML...

<% for( var i = 0; i < 1000; i++ ){ %>
    

        
a div>
        
b div>
        
c div>
    div>
<% } %>

...берет на свой разворот примерно 1,5-2 секунды. Полностью идентичный разворот HTML на ультрасовременном ReactJS с элементами искусственного интеллекта обновления DOM занимает 12 секунд. Это с учетом того, что React разворачивает его только один раз, а остальные 99 - вообще пытается не трогать DOM. Из соображений тонкой оптимизации.

React имеет репутацию невероятно быстрого фреймворка, настолько, что отдельные коллеги вставляют его в приложение на AngularJS, "для ускорения рендеринга". Ви таки будете смеятся, да.

Из сходу плохого про бэкбон мы можем сказать то, что в своем исходном виде он не дает совсем никаких сервисов, облегчащих разработчику жизнь. Он дает правила, но почти никак не вознаграждает за следование им. Это почти голая дисциплина. И вот здесь начинается самое интересное. Что же это за сервисы такие? А я вам скажу - их отсутствие очень просто определить в любом фреймворке. Обратите внимание на две простых вещи:
- В чем люди регулярно ошибаются?
- Каким из хороших практик люди по факту предпочитают не следовать?

То есть, посмотрите на распространенные ошибки, включая ошибки дизайна, и вы сразу увидите слабые стороны фреймворка. Вы можете сказать - ну и что, эти люди просто недостаточно дисциплинированы и умны для него.

Я вам отвечу - это плохая архитектура, для следования которой надо изменять человеческую природу. Люди по природе не любят прикладывать ум для простейших бытовых ситуаций, и не любят дисциплины. Особенно, глупой дисциплины, не имеющей за собой действительно веской причины. А ваш фреймворк - это не веская причина. Смиритесь с этим.

Итак, к чему у нас склонны люди, работая с backbone.
- По разному разворачивают шаблон во View, кто куда горазд.
- По разному подключают subview, и вообще, это делается настолько тяжеловесно, что они предпочитают по возможности не делать subview, раздувая шаблон.
- В шаблоне, постоянно забывают писать model.get( 'name' ), пишут model.name.
- И не в шаблоне - тоже постоянно путаются, и делают присваивания вместо вызовов model.set( 'attr', value );
- В моделях - не пишут defaults для атрибутов. Ленятся. В итоге, понять, что на самом деле лежит в модели, и что туда попадает во время работы программы - практически невозможно.
- В атрибуте, который содержит массив или объект, меняют что-то, и удивляются, почему не обновился View. А модель не замечает глубоких изменений атрибутов. И не должна. Она замечает только model.set
- Не понимают, как им работать с атрибутами типа Date. С ними творится АД, который слишком красочен, чтобы описать его в рамках данной статьи.

Это не полный список. Я, собственно, только начал. Вопросов у команды, начавшей работать с backbone, возникает слишком много. Давайте, руководствуясь критериями, обозначенными в предыдущих статьях, выделим две главные проблемы:
- В Backbone отсутствует рекурсивный паттерн для View. Нет стандартного способа, как сделать View состоящее из других View.
- В Backbone отсутствует рекурсивный паттерн для Model. Нет стандартного способа, как включать другие модели и сложные объекты типа Date в качестве атрибутов.

Без этих двух рекурсивных паттернов, ничего сложного вы с Backbone не сделаете.

Авторы backbone честно и гордо ответят вам - парни, у нас unopinionated framework. Он дает вам свободу. Вы вольны сами решать, какие у вас будут на них ответы. И они правы. По сути, BackboneJS дает слишком слабые правила, чтобы упорядочить групповую разработку. Это конструктор, основа для конкретных фреймворков, которые вы собираете сами, из Backbone, и его расширений, дополняя это своими правилами.

То есть, Backbone сам по себе - это не ответ на вопрос, как делать браузерные приложения. С ним одним приложения сделать нельзя. Это не инструмент команды, а инструмент архитектора.

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

Этого ни один вменяемый архитектор допустить не может. Поэтому, появились Chaplin и Marionette. Эти две штуковины - два варианта, как сделать из Backbone более-менее сносный фреймворк, который имеет свое мнение по части ответов на вопросы выше.

Они оба внятно отвечают на вопрос, как будет структурировано ваше приложение, а также, оба содержат в меру простой паттерн для подключения subview. Однако, оба не дают вложенных моделей и коллекций. Вам предлагается искать для них свое решение.

Год назад я был сильно удручен этими другими решениями, и поэтому мы сделали свое (Backbone.NestedTypes). Это хорошая система моделей, по своим возможностям на данный момент перекрывающая не только остальные плагины Backbone, но и системы моделей близких по идеологии фреймворков, таких как EmberJS. Кстати, знаете, например, что model.set в моделях NestedTypes в десятки раз быстрее в Chrome, в сравнении с моделями Backbone? Нет, это не NestedTypes быстр. Это Backbone-модели чудовищно тормозны.

Однако, дело сейчас не в них - давайте просто будем считать, что с моделями никаких проблем нет. И не в databinding - он для backbone также есть в виде плагинов, если вы захотите.

Сейчас я опишу комплекс проблем, нивелирующих основное преимещество Backbone, с которого мы начали - высокую скорость рендеринга UI. И с ней, в отличии от моделей, ничего поделать нельзя. Для этого нам надо будет углубиться в частные случаи, но иначе будет непонятно, в чем общая проблема. Так что приготовьтесь, оно того стоит.

Давайте предположим, что наш UI дизайнер хочет скругленные углы у элементов форм. Понятное желание, человеческое. Проблема только в том, что IE10 не откликается на соответствующий CSS-стиль. И еще, он хочет заменить уродливый скроллбар в элементе select на красивый. И чекбоксы он хочет тоже свои.

В общем, нет проблем. input надо обернуть в div, и уже этому div скруглить элементы. Сразу возникает проблема с фокусом - он хочет, чтобы элемент подсвечивался рамкой, ну ок - это делается неким глобальным скриптом. Главное для вас, как разработчика, в том, что с этого момента вы уже не можете просто писать input, а должны вставлять на его месте нетривияльный HTML.

То же самое с checkbox - вместо простого input вы должны вставлять некий непростой HTML определенной структуры.

И совсем плохо с select, в котором мы хотим подменить скроллбар и цвет фона. Это адово непростой HTML, который уже затруднительно вставлять в шаблон, и которому нужен продвинутый JS.

Если вы будете применять для каждого такого контрола backbone subview (а subview делать все еще достаточно напряжно в том числе и с Chaplin и Marionette) - вы застрелитесь на второй день. Разработчики ошибаются. Нечеловеческая дисциплина. Не вариант. И вы ищете выход.

Выход называется Web Components, позволяющий определять свои HTML-тэги. Но это новый стандарт, который браузерамм не поддерживается, и поэтому, вы находите Polymer, и чуть позже - x-tag. Та-да!

И тут вы вдруг понимаете, что ваши кастомные тэги разворачиваются асинхронно. То есть, после render, они не готовы к работе. Ну не беда, вы заменяете innerHTML на xtags.innerHTML, чтобы избежать race conditions. Та-да!

И вообще, эти Polymer - это очень модно. Вы на острие технологии. Круто. Та-да!

Только штука в том, что эти кастомные теги разворачиваются DOM-манипуляцией. И это происходит ацки медленно. На самом деле, вы получите скорость рендеринга хуже, чем в модных медленных фреймворках. Она будет настолько медленной, что вы будете замечать это невооруженным глазом. Упс.

Если проблему обобщить, то...
рекурсивный паттерн для subview в Backbone крайне неудобен на микроуровне, что не позволяет вам сделать библиотеку виджетов

А также...
применение Web Components приводит к деградации производительности, нивелируя единственное неоспоримое преимущество Backbone - скорость рендерига

И даже если бы нет, то...
работая в рамках шаблона, вы не можете передавать custom tags сложные объекты, например - модель
Только через текст. Что идиотизм, и очень сильно ограничивает вас в возможностях.

Другими словами, рекурсивный паттерн subview, в условиях того, что view состоит из текстового шаблона и подключения subview на javascipt - крайне неудобен, и значительно усложняет проектирование

Сейчас мы могли бы рассмотреть AngularJS. Но есть одно обстоятельство, которое освобождает нас от необходимости это делать. Дело в том, что уже анонсирован Angular 2.0, который будет обратно не совместим с Angular 1.x, а посему - мало кто в отважится сейчас начинать проект на Angular.

Но если вы настаиваете - в Angular ситуация чуть лучше, так как вы можете определять "директивы", однако наличествует разница между "контроллером" и "директивой", и элемент UI так же расщеплен на HTML и JS. При этом, полностью отсутствует сервис моделей. Как и принципиальное преимущество перед Backbone, которое бы заставило вас выкинуть написанный с ним код. Two-way databinding им не является, он несложно делается и в backbone.

Поэтому, мы рассмотрим Ember, ExtJS, и React. Все они решают обозначенную проблему. Каждый по своему.

архитектура ПО, extjs, reactjs, javascript, angularjs, backbonejs