Статьи

Подключен к магистрали

Вот в чем дело: если вы не можете понять, зачем вам нужна такая среда, как Backbone , скорее всего, вы этого не сделаете! Возможно, вы работаете исключительно над простыми веб-сайтами или базовыми темами WordPress; в этих случаях структурированная структура JavaScript, вероятно, будет излишней.

Однако обязательно наступит день, когда вы поймете, что весь этот код спагетти в тегах script внизу вашей страницы внезапно стал неуправляемым. Кроме того, из-за того, как вы структурировали свой код, его также невозможно протестировать. Gasp! Что делать?

Когда этот день наступит — и он наступит — наведите в своем браузере на Backbonejs.org и сделайте шаг вперед, переходя на следующий уровень зрелости программирования.


Представьте, что Backbone — это небольшой каркас (5,6 КБ, упакованный), который вносит структуру в ваши приложения. Реализуя собственный вид шаблона MVC (фактически, больше похожий на MV *), Backbone предоставляет необходимые инструменты для отделения ваших данных от представления. Если подумать, то, где большинство из нас часто спотыкается, это когда мы отчаянно пытаемся сохранить наши данные. Как правило, это приводит к бесчисленным запросам DOM, так как мы отчаянно выбираем необходимые значения для синхронизации представления и данных нашего приложения. Должен быть лучший способ!

«Вытащи свою правду из DOM». — Джереми Ашкенас (Создатель Backbone.js)

Джереми Ашкенас (Jeremy Ashkenas), создатель Backbone.js, часто выступает за решение этой проблемы — прекратить привязывать ваши данные к DOM. Сделайте так, чтобы ваш взгляд отражал ваши данные, а не наоборот!


По правде говоря, это, вероятно, лучше, если вы даже не пытаетесь преобразовать свои существующие чувства MVC в

Вы будете часто видеть Backbone, а также многие из его родственных структур, называемых MV *, а не MVC. Поскольку концепция стандартного «контроллера» и «представления» на стороне сервера не слишком хорошо переносится в среду JavaScript, это часто может привести к путанице. Это, безусловно, сделал для меня!

По правде говоря, вероятно, будет лучше, если вы даже не попытаетесь преобразовать свои существующие чувства MVC; это только смущает вас. «Представление» в Ruby on Rails — это не то же самое, что «представление» в Backbone. Или «контроллер» в CodeIgniter на самом деле не имеет аналога в Backbone. На самом деле, контроллеров в Backbone не существует! Вместо этого Backbone заимствует кусочки из других структур, поэтому мы часто называем его ближе к MVP (Model, View, Presenter) или MV *: это не MVC и не совсем MVP; это его собственный аромат.


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

В Backbone данные представлены через модель. В качестве примера рассмотрим фотомодель. Эта модель будет определять план фотографии: каков источник фотографии, ее описание, кто изображен на фотографии и т. Д. Мы можем даже применить проверку при необходимости с помощью дополнительного метода validate .

1
2
3
4
5
6
7
var Photo = Backbone.Model.extend({
    defaults: {
        src: ‘images/placeholder.jpg’,
        description: ‘My Image’,
        people: []
    }
});

Имея план, мы можем настроить нашу первую фотографию, создав новый экземпляр модели Photo , например:

1
2
3
4
5
var photo = new Photo({
    src: ‘images/my-image.jpg’,
    description: ‘With friends for dinner’,
    people: [‘John Doe’, ‘Rick James’]
});

Тада — ты создал свою первую модель. Если вам нужно получить или установить информацию из этого экземпляра модели, это так же просто, как использовать методы получения и установки Backbone:

1
2
3
4
5
6
photo.toJSON();
 
photo.get(‘src’);
 
photo.set(‘src’, ‘images/new-path.jpg’);
photo.get(‘src’);

В Backbone, как лучшая практика, представление отвечает за представление одного элемента DOM и любых применимых дочерних элементов, таких как элемент списка или div — все, что вы хотите. Это включает в себя регистрацию любых применимых шаблонов, прослушивание и реагирование на события, а также мониторинг связанных моделей и коллекций на предмет изменений.

Давайте создадим новый вид для самого тега изображения.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
var ImageView = Backbone.View.extend({
    tagName: ‘img’,
 
    initialize: function() {
        this.render();
    },
 
    render: function() {
        this.$el.attr({
            src: this.model.get(‘src’),
            alt: this.model.get(‘description’)
        });
    }
});

Не позволяйте этому коду сбить вас с толку. Это довольно читабельно и элегантно. Мы сделаем это шаг за шагом.

Для начала tagName определяет, какой элемент DOM представляет представление. В этом случае мы определяем представление для одного элемента img , но мы могли бы с таким же легкостью связать его с элементом, который уже существует в DOM, через свойство el .

1
2
3
var ImageView = Backbone.View.extend({
    el: ‘#my-image’
});

За кулисами Backbone будет извлекать указанный элемент ( #my-image ) из DOM, кэшировать его и делать доступным через ImageView.el и ImageView.$el . Последняя версия, как вы могли ожидать, — это элемент, обернутый в jQuery (или, возможно, Zepto, если вы предпочитаете эту библиотеку над jQuery).

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

Наконец, метод render отвечает за построение вывода. В этом случае атрибуты src и alt изображения будут установлены равными данным, которые хранятся в ассоциированной модели представления.

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

1
2
3
4
5
6
7
8
var photo = new Photo({
    src: ‘images/my-image.jpg’,
    description: ‘With friends for dinner’,
    people: [‘John Doe’, ‘Rick James’]
});
 
var imageView = new ImageView({ model: photo });
imageView.el;

Это может быть невероятно мощным, если вы думаете об этом! В Backbone тривиально обновлять представление всякий раз, когда изменяется его соответствующая модель, предполагая отношение «один к одному». Давайте послушаем, когда данные (или модель) будут изменены. Когда это так, мы должны соответствующим образом обновить элемент.

Следующий код может быть добавлен к методу initialize ImageView .

01
02
03
04
05
06
07
08
09
10
11
12
initialize: function() {
    // Listen for when the model is updated
    this.model.on(‘change’, this.render, this);
    this.render();
},
 
render: function() {
    this.$el.attr({
        src: this.model.get(‘src’),
        alt: this.model.get(‘description’)
    });
}

Не стоит недооценивать, насколько эффективной может быть эта реализация PubSub. Когда модель изменяется, она делает объявление, так сказать. «Любой, кто заинтересован — я только что был изменен!» На наш взгляд, с помощью одной строки кода мы можем подписаться на это объявление и ответить соответствующим образом, обновив атрибуты изображения.

1
this.model.on(‘change’, this.render, this);

В этом случае мы повторно вызываем метод render и снова генерируем атрибуты.

Давайте проверим это.

1
2
3
4
5
var imageView = new ImageView({ model: photo });
imageView.el;
 
photo.set(‘src’, ‘some-new-path.jpg’);
imageView.el // <img src=»some-new-path.jpg» alt=»With friends for dinner»>

Отлично! При необходимости вы также можете с легкостью подключить прослушиватели событий уровня DOM через объект events который Backbone автоматически найдет и установит для вас. Возможно, вы хотите отредактировать изображение, когда на него нажали:

1
2
3
4
5
6
7
events: {
    ‘click’: ‘edit’
},
 
edit: function() {
    // edit the image
}

Так как это конкретное представление представляет один элемент img , мы можем придерживаться click' , однако вы часто обнаружите, что View.el содержит вложенные дочерние View.el . В этих случаях вы можете указать селектор после типа события, например:

1
2
3
4
5
events: {
    ‘click span’: ‘doSomething’
},
 
doSomething: function() {}

Приведенный выше код с использованием View.el в качестве контекста прикрепит событие click к любому дочернему span .

Что хорошего в модели для одной фотографии? Давайте сохраним все фотомодели в коллекции Photos . Думайте о коллекциях как о массивах с добавленным сахаром и удобными методами, благодаря единственной жесткой зависимости Backbone, Underscore.js.

1
2
3
var Photos = Backbone.Collection.extend({
    model: Photo
});

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

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

1
2
3
4
5
var photos = new Photos([
    { src: ‘image1.jpg’, description: ‘Vacation 2012’ },
    { src: ‘image2.jpg’, description: ‘My best friend’ },
    { src: ‘image3.jpg’, description: ‘Anniversary party’ }
]);

Обратите внимание, что на этот раз кажется, что мы просто передаем несколько объектов в коллекцию Photos . Однако за кулисами каждый object в array будет преобразован в модель Photo .

Подобно моделям, коллекции будут объявлять о любых изменениях, например, когда модель добавляется или удаляется. Это означает, что вы можете прослушивать, например, когда новый элемент добавляется в коллекцию. Когда это происходит, вы можете компенсировать это, обновляя DOM соответственно новым элементом.

1
this.collection.on(‘add’, this.appendItem);

Кроме того, как отмечалось выше, в коллекции содержится много сахара. Например, скажем, нам нужно получить свойство src из каждой модели в коллекции. Это легко с методом pluck !

1
photos.pluck(‘src’);

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

Обратитесь к документации Underscore.js для большего количества примеров использования.

На данный момент у нас есть коллекция фотографий, но теперь нам нужен новый вид, который будет отвечать за представление контейнера фотографий. Помните, что ImageView отвечает только за один элемент изображения. Давайте создадим новый вид для упаковочного списка фотографий.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
var PhotosView = Backbone.View.extend({
    tagName: ‘ul’,
 
    className: ‘photos’,
 
    initialize: function() {
        this.render();
    },
 
    render: function() {
        var imageView;
 
        this.collection.forEach(function(model) {
            imageView = new ImageView({ model: model });
            this.$el.append( $(‘<li>’).html(imageView.el) );
        }, this);
    }
});

На этот раз мы создаем представление для обертывающего элемента <ul class="photos"> . Когда мы вызываем render() , метод отфильтрует все модели, содержащиеся в связанной коллекции (в нашем случае три), создаст новый ImageView для каждого — который создает элемент изображения — и затем мы добавим этот сгенерированный элемент изображения в неупорядоченный список .photos .

Помните: не беспокойтесь о перекрашивании или перекомпоновке. PhotosView.el еще не PhotosView.el в DOM.

1
photosView.el.parentNode // null

Итак, давайте все вместе!

01
02
03
04
05
06
07
08
09
10
11
12
// Create a collection of Photo models.
var photos = new Photos([
    { src: ‘image1.jpg’, ‘description’: ‘Vacation 2012’ },
    { src: ‘image2.jpg’, ‘description’: ‘My best friend’ },
    { src: ‘image3.jpg’, ‘description’: ‘Anniversary party’ }
]);
 
// Create a new PhotosView, and pass in the photos collection
var photosView = new PhotosView({ collection: photos });
 
// Throw our list of photos into the DOM.
$(‘body’).html(photosView.el);

Поначалу это может показаться запутанным, но, безусловно, тем легче будет работать с Backbone. В предыдущем фрагменте кода мы начнем с создания коллекции моделей Photo . Далее мы создаем новый экземпляр контейнера PhotosView . При отображении это представление фильтрует все элементы в соответствующей коллекции, создает новые экземпляры ImageView и добавляет полученные элементы в неупорядоченный список. Наконец, мы берем полученный DOM-фрагмент и бросаем его в DOM. Не слишком сложно, а? И посмотрите: структура!


Мы едва поцарапали поверхность, на которую способна Backbone. Чтобы продолжить обучение, обратитесь к следующим книгам, скринкастам и учебным пособиям.


Магистраль иногда критикуют за недостаточное предложение. Он не реализует какую-либо конкретную структуру и не предлагает компонентов пользовательского интерфейса, которые вы можете получить из Dojo или jQuery UI. По иронии судьбы, несмотря на эту критику, именно это делает Backbone таким фантастическим. Он не навязывает вам свое мнение. Он прекрасно выполняет одну работу и одну работу: обеспечивает структуру для ваших приложений. После этого вы сможете выяснить, как лучше всего использовать Backbone в существующих проектах.