8 наиболее распространенных ошибок разработчиков Backbone.js
Backbone.js

8 наиболее распространенных ошибок разработчиков Backbone.js

Оригинал: Top 8 Common Backbone.js Developer Mistakes, Mahmud Ridwan

Backbone.js  — это минималистичный фреймворк, предоставляющий простой набор структур данных и функций, которые вы можете использовать для создания клиентской части веб-приложения. Со схожей структурой моделей и представлений вы уже, вероятно, встречались в бэкенд-разработке. Модели и коллекции в Backbone.js очень простые, но содержат ряд полезных функций, например, возможность легко интегрировать их с REST JSON API. В то же время они являются достаточно гибкими и могут использоваться почти в любой практической ситуации.

В этой статье мы рассмотрим некоторые общие ошибки разработчиков , делающих первые шаги в изучении Backbone.js, а также способы избежать таких ошибок.

Ошибка #1: Игнорирование функциональных возможностей Backbone.js

Backbone.js является минималистичным, но он (вместе с Underscore.js) обеспечивает множество функций, которые легко покрывают самые основные потребности, возникающие при разработке современного веб-приложения. Одна распространенная ошибка, которую делают начинающие разработчики, заключается в том, что они воспринимают Backbone.js как еще один клиентский MVC-фреймворк. Хотя это довольно очевидно, но когда дело касается Backbone.js, действительно важно тщательно изучить его основы. Фреймворк очень небольшой по размеру, но именно это делает его отличным кандидатом для детального изучения. Стоит изучить его небольшой и прекрасно документированный исходный код .

Backbone.js предоставляет минимум, необходимый для создания структуры веб-приложения, и в этом есть определенное преимущество. С его расширяемостью и множеством плагинов , Backbone.js можно использовать для создания по-настоящему удивительных веб-приложений. Некоторые из наиболее очевидных особенностей Backbone.js доступны через модели, коллекции и представления. Роутер и Backbone.history обеспечивают простой, но элегантный механизм маршрутизации на стороне клиента. Библиотека Underscore.js является зависимостью Backbone.js, она прекрасно интегрирована в фреймворк. Во-первых, модели и коллекции используют ее возможности, а во-вторых, функции самой библиотеки в вашем распоряжении.

Исходный код фреймворка настолько хорошо написан и документирован, что можно читать его, попивая чашечку кофе. Изучение исходников будет особенно полезно новичкам, поскольку они не только узнают многое о том, как работает фреймворк внутри, но и познакомятся с набором передовых методов разработки на JavaScript.

Ошибка #2: Модификация DOM в обработчиках событий

При первом опыте с Backbone.js наш код часто не соответствует рекомендациям. Например, мы склонны обновлять представление в обработчиках событий, как привыкли это делать в jQuery на простых веб-сайтах. Backbone.js предназначен для правильного разделения обязанностей в приложении. Рассмотрим пример обновления представления в обработчике произвольного события DOM:


var AudioPlayerControls = Backbone.View.extend({
    events: {
        'click .btn-play, .btn-pause': function(event) {
            $(event.target).toggleClass('btn-play btn-pause')
        }
    },
    // ...
})

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


var AudioPlayerControls = Backbone.View.extend({
    events: {
        'click .btn-play, .btn-pause': function(event) {
            this.model.set('playing', !this.model.get('playing'))
        }
    },
    initialize: function() {
        this.listenTo(this.model, 'change', this.render)
        this.render()
    },
    // ...
})

<button class="btn btn-<%- playing ? 'pause' : 'play' %>"></button>

Могут возникать редкие ситуации, когда прямые манипуляции с DOM имеют смысл, но затраты, связанные с поддержанием такого кода, почти никогда не оправдываются. Использовать Backbone.js для чего-то подобного — это ошибка.

Ошибка #3: Недооценка стоимости отрисовки

В Backbone.js настолько легко отрисовать DOM по желанию или в обработчике события, что мы часто не задумываемся, насколько это влияет на общую производительность веб-приложения. Есть много ситуаций, приводящих к слишком частому вызову метода render представления. Часто это может показаться не критичным, так как современные браузеры становятся все более производительными. Но с ростом веб-приложения и объемов данных падение производительности становится все более и более очевидным.

Можно наблюдать такую ситуацию с помощью выдуманного примера, где мы отображаем небольшую коллекцию моделей в виде списка:


var AudioPlayerPlaylist = Backbone.View.extend({
    template: _.template('<ul> <% _.each(musics, function(m) { %> <li><%- m.title %></li> <% }) %> </ul>'),
    initialize: function() {
        this.listenTo(this.collection, 'add', this.render)
    },
    // ...
})

В этом примере, мы отрисовываем представление всякий раз, когда модель добавляется в коллекцию. Это будет прекрасно работать. Однако, поскольку событие «add» срабатывает каждый раз, когда модель добавляется в коллекцию, представьте себе получение большого списка моделей с сервера. Метод render будет вызываться несколько раз для каждой модели. Достаточно большая коллекция может повесить приложение. А иногда достаточно и небольшой коллекции при высокой сложности рендеринга представления.

Быстрое решение этой проблемы заключается в том, чтобы просто не вызвать метод render для каждой модели, которая добавляется в коллекцию. В таких ситуациях, модели будут добавляться в коллекцию партиями, и можно сделать так, чтобы метод render не вызывался повторно в течение определенного количества времени. В Underscore.js есть для этого удобная функция «_.debounce». Все, что вам нужно, это изменить обработчик события:


this.listenTo(this.collection, 'add', _.debounce(_.bind(this.render), 128))

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

В большинстве случаев, это будет считаться быстрым фиксом. На самом деле, есть более подходящие способы избежать подобных ситуаций. Разработчики Trello рассказывают в блоге о своем опыте и подходах к улучшению производительности Backbone.js.

Ошибка #4: Неиспользуемые обработчики событий

Неиспользуемые обработчики событий могут возникнуть независимо от того, какой JavaScript-фреймворк вы используете. Несмотря на то, что Backbone.js позволяет легко избежать этой проблемы, подобная ошибка по-прежнему оставляет потенциальную возможность утечки памяти в приложении. Компонент Event в Backbone.js позволяет объектам JavaScript легко реализовать функционал, основанный на событиях. В представлениях, где большая часть функционала основана на событиях, легко сделать такую ошибку:


var AudioPlayerControl = Backbone.View.extend({
    initialize: function() {
        this.model.on('change', _.bind(this.render, this))
        // ...
    },
    // ...
})

Навешивание события в этом фрагменте кода не очень отличается от первого примера. Мы всего лишь изменили «this.listenTo(this.model, ...)» на «this.model.on(...)». Поскольку метод «.on()» используется в других библиотеках JavaScript, он является более более привычным и часто используется начинающими разработчиками. И это хорошо, если потрудиться вызвать метод «.off()» для удаления обработчика, когда он уже не нужен. Но это делается редко, что и является источником утечки памяти.

Backbone.js предлагает простой способ решить эту проблему за счет использования метода «object.listenTo()». Он позволяет объекту, для которого вызван «listenTo()» самому отслеживать события, на которые он подписан, а также легко удалить обработчик. Представления, например, автоматически перестают отслеживать все события после вызова метода «remove()».

Ошибка #5: Создание монолитных представлений

Минимализм Backbone.js обеспечивает огромную гибкость в плане архитектуры веб-приложения. Модели, коллекции и представления являются строительными блоками веб-приложения. Важно держать их легковесными и специфичными, насколько это возможно. Но представления часто становятся тяжелыми с точки зрения кода. На самом деле, лучше не делать огромные монолитные представления, которые выполняют все функции приложения. Одно огромное представление «AudioPlayer» со всей логикой можно логически разделить на несколько представлений, например представление для плейлиста, представление для элементов управления, представление для визуализатора и т.д. Состав таких представлений зависит от конкретного приложения.

При правильном разделении, когда каждое представление делает что-то конкретное и делает это правильно, разработка веб-приложений с Backbone.js становится приятнее. Такой код легче поддерживать, его легче расширять или изменять в будущем. Есть и другая крайность, поэтому в конечном итоге важно не переусердствовать. Представления Backbone.js предназначены для того, чтобы сделать удобной работу с моделями или коллекциями. Это может стать подсказкой, как правильно структурировать приложение. Ян Шторм Тейлор поделился ценными идеями в блоге, которые можно иметь в виду при реализации представлений.

Ошибка #6: Непонимание того, что Backbone.js может быть адаптирован к не-RESTful API

Backbone.js работает с JSON на основе RESTful API из коробки. Все, что вам нужно — это jQuery (или его замена, например, Zepto). Тем не менее, Backbone.js чрезвычайно расширяем. На самом деле, он может быть адаптирован для использования в других типах интерфейсов или с другими типами форматов кодирования.

Компонент Backbone.Sync отвечает за взаимодействие фронтенда и бэкенда. Он предоставляет набор атрибутов, которые вы можете легко переопределить, чтобы настроить способ взаимодействия Backbone.js с API. Механизм синхронизации по умолчанию легко изменить, например, сохранять данные в localStorage вместо бэкенд-сервиса.

Существует множество плагинов , позволяющих кастомизировать механизм синхронизации Backbone.js. Например, плагин Backbone.dualStorage позволяет использовать одновременно и бэкенд-сервис, и localStorage для сохранения данных. Когда приложение переходит в автономный режим, плагин использует localStorage для сохранения и получения данных, а также отслеживает изменения, которые синхронизируются с сервером позже, когда появится доступ в интернет.

Хотя использовать Backbone.js с RESTful-бэкендом просто, это не значит, что только с ним и может работать Backbone.js. С некоторыми изменениями в механизме синхронизации, вы можете адаптировать его к широкому спектру API и форматов кодирования.

Стоит отметить, что другие части Backbone.js являются не менее гибкими и настраиваемыми. Например, вы не обязаны использовать шаблонизатор по умолчанию, который поставляется с Underscore.js. Вы даже не должны использовать компонент представления Backbone.js и можете заменить его чем-то совершенно другим, если хотите.

Ошибка #7: Сохранение данных в представлении вместо модели

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


var AudioPlayerVisualizer = Backbone.View.extend({
    events: {
        'click .btn-color': function(event) {
            this.colorHex = $(event.target).data('color-hex')
            this.render()
        }
    },
    // ...
})

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

Представьте, что вы на самом деле храните переменные отслеживания состояния в представлении и должны вызвать метод render каждый раз при их изменении. Потеря одного вызова render может перевести приложение в некорректное состояние, когда пользователь видит на экране не то, что хранится в данных. Кроме того, выделив переменные состояния в модель, вы можете отслеживать их изменение сразу в нескольких представлениях и одновременно вызывать метод render.

Ошибка #8: Использование jQuery .on() вместо делегирования событий

Backbone.js имеет, на мой вгляд, один великолепный способ обработки событий DOM. Неиспользование его добавляет кучу проблем. Функция добавления обработчика события jQuery .on() удобна, но часто доставляет много хлопот в долгосрочной перспективе. Например, когда элементы удаляются из DOM, jQuery автоматически удаляет все обработчики событий, связанные с использованием .on(). Это означает, что любой обработчик события DOM, который вы привяжете в представлении, необходимо будет привязать заново, если вы удалите корневой элемент из DOM’a, а потом вернете его обратно.


var AudioPlayerControls = Backbone.View.extend({
    events: {
        'click .btn-play, .btn-pause': function() { /* ... */ },
        'click .btn-prev': function() { /* ... */ },
        'click .btn-next': function() { /* ... */ },
        'click .btn-shuffle': function() { /* ... */ },
        'click .btn-repeat': function() { /* ... */ }
    },
    // ...
})

В примере выше, чтобы заново привязать обработчики событий, нужно будет всего лишь вызвать метод delegateEvents() представления. Очень важно понимать, как эти события связаны. Вместо того, чтобы связывать события с элементами, указанными в селекторе, Backbone.js на самом деле связывает обработчик события с корневым элементом представления. Это отлично подходит для решения большинства задач. Изменение или замена дочерних элементов DOM не требует повторного связывания событий для новых элементов. Существующие обработчики продолжать работать.

В то же время, это мешает отслеживать некоторые события. Например, событие «scroll» объекта window или дочернего элемента. В случае дочернего элемента, вы можете создать вложенное представление, где этот элемент будет корневым и навесить обработчик в нем.

Заключение

Backbone.js, будучи очень компактным, но расширяемым фреймворком, является отличным выбором для веб-приложений, которые требуют большой гибкости. В отличие от таких фреймворков, как Angular.js и Ember.js, в которых всегда есть четкий ответ, как сделать то, что вы хотите сделать, Backbone.js делает шаг назад, дает вам мощный набор инструментов и позволяет самостоятельно решать, как их использовать. Я надеюсь, что эта статья о Backbone.js для начинающих поможет вам избежать некоторых распространенных ошибок и создать на его основе что-то удивительное.

Рассылка
Подпишитесь на рассылку и получайте дайджест новостей и статей.
Никакого спама!
Подписаться