Блог Мазепина Василия

Пишу о том, что кажется интересным

Здравствуй, Backbone.js

2016-07-31 21:20 | Комментарии

Доброго времени суток, коллеги и гости. После изучения Underscore, мне хотелось дальше изучать javascript-библиотеки, которые мне в последствии могут понадобиться. После недолгого перерыва, началось изучение. Добро пожаловать под кат…

Итак, с чего же нам начать? Обратимся к официальной документации http://backbonejs.org, в которой достаточно хорошо и понятно задокументированы функции.

Что это такое?

Согласно википедии, Backbone — JavaScript-библиотека, основанная на шаблоне проектирования Model-View-Presenter (MVP), предназначена для разработки веб-приложений с поддержкой RESTful JSON интерфейса.

А зачем?

Случалось ли вам сталкиваться с таким кодом, где приходится отправлять CRUD Ajax запросы к серверу, отрисовывать содержимое БД, синхронизировать данные с представлением посредством дополнительных действий, событий. Зачастую на проектах это делается сугубо через jQuery, и всё это превращало ваш код в “спагетти”, что не есть хорошо.

Так Backbone позволяет нам весь этот “спагетти-код” систематизировать, привести в порядок, и, следовательно, сделать более удобным для чтения и восприятия. Нужно ли оно? Решать вам…

Если всё же нужно, то с чего начнем?

А начнем мы немного с матчасти. Библиотека держится на 4-х китах:

  1. Model - Модель, которая отвечает за структуру данных нашего приложения, над которыми будет идти обработка. Включает в себя механизм сохранения данных, установку правил валидации перед установкой данных, а так же синхронизацию данных.
  2. Collection - Коллекция, цель которой, хранить в себе (не поверите) список моделей, с целью работы над ним. Обработка в данном случае не сильно отличается от коллекций в Underscore. Можно добавить модель в коллекцию, убрать ее оттуда, обновить позицию, отслеживать типичные для коллекции события.
  3. View - Вид, представление. Вот в нем то и вся прелесть! Может отображать вашу модель, может отображать вашу коллекцию. Может обрабатывать события действий над формами. Вы сами пропишите в каких тегах необходимо отображать данные! Нужен шаблонизатор? Да пожалуйста, механизм шаблонизации поддерживается! Для связки представления и данных достаточно будет указать модель либо коллекцию в качестве источника, прописать пару строк отрисовки, и представление само будет обновляться, без лишних усилий.
  4. Router - Роутер, позволяет обрабатывать рауты, к которым обратился пользователь. Отличное решение для сайтов - одностраничников.

Модель

Давайте рассмотрим пример создания модели человека

Объявление модели
1
2
3
4
5
6
7
8
9
10
11
12
var Person = Backbone.Model.extend({
    // Указываем аттрибуты
    defaults: {
        name: 'Dima',
        age: 23,
        job: 'Developer'
    },
    // мы можем создавать наши методы и свойства для наших моделей
    walk: function () {
        return this.get('name') + ' is walking';
    }
});

Как мы видим, мы создаем обьект модели, расширенный из Backbone.Model методом extend, в котором (пере)определяем свойства и методы исходной модели.

Валидация

Для валидации данных в модели, необходимо лишь назначить функцию для свойства модели validate, в котором параметром модели будут все атрибуты модели, включая изменение. Валидация срабатывает при сохранении модели (метод Model.save()), либо при назначении атрибута методом Model.set({some_attr: value}, {validate: true}). И в этой функции необходимо просто возвращать какую-либо строку с ошибкой, если таковая имеется, и это уже будет считаться ошибкой валидации. Хотите примера? Их есть у меня:

Валидация
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var Person = Backbone.Model.extend({
    defaults: {
        name: 'Dima',
        age: 23,
        job: 'Developer'
    },
    validate: function (attrs) {
        if (attrs.age <= 0) {
            return 'Возраст должен быть больше 0'
        }
        if (!attrs.name) {
            return 'Имя не должно быть пустым'
        }
    },
    walk: function () {
        return this.get('name') + ' is walking';
    }
});

Коллекции

Модель по сути своей может отображать лишь одну запись, и содержит знания лишь о ней. Но ведь в БД у нас множество записей. Тут нам на помощь придут коллекции.

Создание списка(коллекции) людей
1
2
3
4
5
//Список людей
var PeopleCollection = Backbone.Collection.extend({
     //Модель по умолчанию
      model: Person
});

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

Представления

Без представлений Backbone был бы не полноценным, поэтому перейдем к ним. Для создания простейшего представления необходимо расширить стандартный Backbone.View, указав свои свойства:

Создание представления
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var PersonView = Backbone.View.extend({
    // Работает как конструктор класса - вызывается при создании обьекта данного типа
    initialize: function () {
    },
    tagName: 'li', // тег, в который будет обернуто отображение
    className: 'person', // класс, который будет иметь tagName 
    id: 'some-person', // id, который будет иметь tagName 
    render: function () {
        var model = this.model;
        // Самое простое, без шаблонизаторов.
        this.$el.html(model.get('name') +'(' + model.get('age') + ' - '+ model.get('job') + ' )');
        return this; //Для цепных вызовов
    }
});
// Создание представления для определенной модели
var person = new Person;
// Прикрепляем моель к вьюхе
var view = new PersonView({model:person});

Важно отметить, что свойства tagName, id, className, el, и events могут быть заданы в виде функций, что придает гибкости. Еще не менее важно понимать, что любой View имеет атрибуты $el и el. По сути связь между ними такая: $(el) = $el, где $ = знакомый нам jQuery.

View.el это по сути место, куда должен лечь html представления, оно же и используется для вставки представления на страницу:

Отобраение данных на странице
1
$(document.body).append(view.render().el);

Метод render как раз таки призван заполнить el либо самостоятельно, либо используя шаблонизаторы. Для того чтобы можно было использовать цепные вызовы, принято возвращать в нем this(само представление) Коллекции тоже можно прикреплять к представлению, и отрисовать, вот как это делается:

Отрисовка представления коллекции
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Вид списка людей
var PeopleView = Backbone.View.extend({
    tagName: 'ul',
    initialize: function () {
    },
    render: function () {
        // Пройтись по всему списку и срендерить PersonView
        this.collection.each(function (person) {
            var personView = new PersonView({model:person});
            //Обратите внимание на цепной метод
            this.$el.append(personView.render().el);
        }, this);
        return this;
    }
});
var peopleView = new PeopleView({collection: peopleCollection});
$(document.body).append(peopleView.render().el);

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

Обработка сабмита формы
1
2
3
4
5
6
7
8
9
10
11
12
addPersonForm = Backbone.View.extend({
        el: '#add-person-form',
        events: {
            'submit': 'submit'
        },
        submit: function (event) {
            event.preventDefault();
            var PersonName = $(event.currentTarget).find('input[type=text]').val();
            var person = new Person({name:PersonName});
            this.collection.add(person);
        }
    });

Роутер

Основная цель роутера - обработать различные урлы. Важно заметить, что имеются ввиду урлы-ярлыки вида #url:

Роутер
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    Router = Backbone.Router.extend({
        routes: {
            '': 'index',
            'read': 'read'
        },
        index: function () {
            console.log('Всем привет от индексного роута');
        },
        read: function () {
            console.log('Это роут Read');
        }
    });
    new App.Router;
    Backbone.history.start(); // Чтобы Backbone отслеживал историю

Синхронизация данных

У модели в Backbone есть еще один очень важный плюс - возможность синхронизировать представление с источником данных. Объявим коллекцию, указав свойство url(Может быть функцией)

Инициализация коллекции книг
1
2
3
var Books = Backbone.Collection.extend({
  url: '/books'
});

Тогда автоматически будут отправлены запросы к серверу:

GET /books/ …. collection.fetch(); - Заполнение коллекции данными с сервера
POST /books/ …. collection.create(); - добавление записи в БД
GET /books/1 … model.fetch(); - Заполнение модели данными с сервера
PUT /books/1 … model.save(); - Запись данных на сервер, обновление.
DEL /books/1 … model.destroy(); - Удаление записи из таблицы.

Неплохо, да? Все что остается программисту - реализовать данные методы на сервере.

Теперь о своем …

Не смог удержаться, чтобы не написать свое собственное мини приложение, использующее все ранее полученные знания. Напишу - ка я менеджер контактов. Но с чего начать?

1. Установка

На официальной странице доступны для скачивания 3 версии:

  • Development Version - стабильная версия без минификации, включающая тонну комментариев.
  • Production Version - минифицированная стабильная версия, за счет чего гораздо легче по размеру.
  • Edge Version - Нестабильная разрабатываемая в данный момент версия. На сайте об этом написано, используйте ее на свой страх и риск.
    В качестве зависимостей библиотеке немного нужно - стабильный Underscore.
    Так же можно установить библиотеку через различные менеджеры пакетов:
    bower install backbone
    npm install backbone

2. Реализация бекенда

Я реализовал бекенд на фреймворке Silex, однако в качестве БД я использовал обычный JSON-файл, так как заморачиваться с доступом не хотелось. На реальных проектах конечно же нужна будет работа с СУБД.

3. Реализация фронтенда

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

  1. Модели, Виды и Представления для удобства разнесены в различные файлы, дабы не создавать путаницу. Читается реально легче. Необходимо лишь правильно потом эти файлы загрузить.
  2. В качестве шаблонизатора использовал Handlebars. Ни разу не работал с ним ранее, просто увидел про него в одной из статей.

4. А можно всё посмотреть?

Можно, вот инструкция:

В терминале
1
2
3
4
5
git clone https://github.com/liven/backbone-learn.git backbone
cd backbone
composer install
bower install
php -S localhost:8000

И тогда по адресу localhost:8000 вы получите пример реализации простейшего менеджера контактов, ну либо просто перейдите сюда.

Пора прощаться

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

Комментарии