7 проверенных в бою правил для написания веб-приложений на Backbone.js

В нашей компании (Bizzabo) мы используем Backbone.js на протяжении двух лет. Мы начали с маленького веб-приложения, и сейчас на нём написаны уже несколько с использованием Require.js и Handlebars.js. Приложения на Backbone не придерживаются строгой архитектуры. Основная идея, которую несёт документация: используйте инструменты этого фреймворка так, как вам хочется. Благодаря такому подходу Backbone хорош для абсолютно разных задач, и на нем очень просто начать писать приложения. Однако с другой стороны, это приводит к тому, что новички совершают ошибки в самом начале работы с ним. В процессе мы натыкались на различные подводные камни и находили для них лекарство. Для того чтобы не повторить наши ошибки, мы предлагаем вам несколько правил и подсказок.

1. Представления без данных

Данные хранятся в моделях, а не представлениях. В следующий раз когда вы увидите данные в представлении или, еще хуже, в DOM, переместите их в модель. Если у вас нет модели для этих данных, создайте её:

this.viewState = new Backbone.Model();

Это позволит отслеживать события изменения данных или даже синхронизировать их с сервером для работы с ними в реальном времени.

2. События DOM влияют только на модели

Когда вызывается событие DOM, например, при нажатии на кнопку, меняйте только модель, не представление. Изменение DOM без изменения модели означает, что вы храните данные в DOM. Данное правило позволит вам избежать несогласованных данных. Например, если нажали на ссылку «Подробнее», не разворачивайте ваше представление, просто измените данные модели:

this.viewState.set('readMore', true);

«Отлично, но как тогда изменить представление?» — спросите вы. Отличный вопрос, ответ на него в следующем правиле.

3. DOM изменяется только при изменении модели

События — очень крутая штука, используйте их. Наиболее простой подход — перерисовывать всё представление при любом изменении модели:

this.listenTo(this.stateModel, 'change', this.render);

Более правильный подход — отрисовывать только необходимые вещи:

this.listenTo(this.stateModel, 'change:readMore', this.renderReadMore);

Представление всегда синхронизировано с моделью. Не имеет значения, что повлияло на изменение модели: действие пользователя, получение новых данных или отладка приложения в консоли — представление всегда будет в актуальном состоянии.

4. Не забывайте своевременно удалять обработчики событий

Когда представление удаляется из DOM при помощи метода remove, необходимо удалить все связанные с ним обработчики событий. Если вы используете метод on для назначения обработчика события, используйте метод off для его удаления. Если этого не сделать, сборщик мусора не сможет освободить память, и производительность вашего приложения снизится. В Backbone есть специальный метод listenTo, который позволяет отслеживать, к каким обработчикам привязаны события в представлении и удалять связи при вызове метода remove. Вам достаточно вызвать метод stopListening перед удалением представления из DOM.

// Допустимо:
this.stateModel.on('change:readMore', this.renderReadMore, this);

// Так круче:
this.listenTo(this.stateModel, 'change:readMore', this.renderReadMore);

5. Всегда сохраняйте возможность использовать цепочки

Всегда возвращайте this внутри методов render и remove. Это позволит использовать цепочки:

view.render().$el.appendTo(otherElement);

Никогда не нарушайте данное соглашение.

6. События лучше чем обратный вызов

Всегда лучше отслеживать события, чем ждать обратный вызов. В Backbone у моделей по умолчанию срабатывают события sync и error, и мы можем это использовать. Сравните два разных подхода:

model.fetch({
    success: handleSuccess,
    error: handleError
});

И второй вариант:

view.listenTo(model, 'sync', handleSuccess);
view.listenTo(model, 'error', handleError);
model.fetch();

Методы handleSuccess и handleError будут вызваны независимо от того, как и где была получена модель.

7. Представления имеют свою область видимости

Представление никогда не должно работать с той частью DOM, которая ей не принадлежит. У каждого представления есть ссылка на свою часть DOM — el, либо через элемент jQuery $el. Это означает, что вы никогда не должны использовать jQuery напрямую:

$('.text').html('Thank you');

Вместо этого, всегда используйте свою область видимости DOM:

this.$('.text').html('Thank you');
 
// То же самое: 
// this.$el.find('.text').html('Thank you');

Если вам надо работать с другим представлением, используйте вызов события (trigger) и позвольте другому представлению самому заботиться о своей части DOM. Кроме того, вы можете использовать Backbone в качестве системы подписки и публикации событий. В данном примере мы предотвращаем прокрутку страницы:

var BodyView = Backbone.View.extend({
    initialize: function() {
        this.listenTo(Backbone, 'prevent-scroll', this.preventScroll);
    },
 
    preventScroll: function(prevent) {
        // класс .prevent-scroll использует CSS свойство: overflow: hidden;
        this.$el.toggleClass('prevent-scroll', prevent);
    }
});
 
// И далее где-нибудь в нужном месте кода:
Backbone.trigger('prevent-scroll', true); // запрет прокрутки
Backbone.trigger('prevent-scroll', false); // разрешение прокрутки

И ещё одно

Вы можете многое узнать и понять, просто изучая код Backbone. Исследуйте исходный код с аннотациями для понимания всей его «магии». Библиотека достаточно мала, и беглое изучение не должно занять у вас более 10 минут.

Эти правила помогут писать чистый, красивый и легко читаемый код.