0,0
рейтинг
25 августа 2011 в 13:39

Разработка → Backbone.js для «чайников»

Backbone.js для чайников
Как то поздним вечерком мне пришла мысль изучить Backbone.js и привязать его к уже написанному на jQuery сервису. Сервис уже серьёзно расширился и меня достало это нагромождение обработчиков кликов, запросов и логики. Поэтому, я как усердный школьник полез в официальную документацию. Но либо я тупой, либо мой английский меня подкачал, либо то и другое вместе, но я не черта не понял. Я прочитал уже второй раз, внимательно, и для особо одарённых мест использовал google translate. Прочитал также и пример ToDo List. Всё показалось понятно, ровно до той поры пока я не стал писать. После чего я взял всё что нашел по этой библиотеке, как на английском так и переводы. Прочтя кипу документации я решил, что сейчас вроде всё понял. Я напрягся, но… Не вышел каменный цветок у мастера Данилы, т.е. вышло, но это явно был не цветок, и камень как то неправильно пах. Тогда, как прилежный ученик, я решил написать «Hello, KittyWorld» с нуля. Попутно комментируя и сохраняя шаги в hg, у меня получилось введение в backbone.js framework для таких как я, особо одарённых.

Задача.


Выберем простую задачу. Написать Hello, World? Слишком просто, также как и написать Hello, <имярёк>. Может напишем клиент GTD с авторизацией и оффлайн хранилищем? Такое уже есть и оно не помогает понять нашу “хребтовую кость”. Сделаем проще. Создадим страницу с 3 состояниями. В первом состоянии человек вводит имя пользователя, во втором состоянии его поздравляют, если введённое имя найдено, в третьем состоянии огорчают, если имя не найдено. По-моему, данная задача учебней и проще некуда, да и в общем позволит посмотреть и проверить почти всё что есть в backbone.

Все шаги сохраним через mercurial. Поэтому, читая какой либо шаг вы можете распаковать zip архив (+ dropbox, если на народе удалят), зайти в каталог и перейти на нужную ревизию при помощи команды
hg update --rev <номер ревизии>

После чего посмотреть на код и понять то, что вам не понятно :)

Шаг 0. Структура и шаблон (rev 0)


структура
Структуру будем использовать академическую, такую как на картинке. Сознаюсь, я не знаю кто автор этой священной пули, у кого я слизал эту корову, но использую во всех заготовках. Один файл index.html. В папке css лежат стили, в папке i лежат картинки, в js — скрипты. Одновременно закинем в скрипты jquery, underscore и backbone к скриптам.
Шаблон html — пустая страничка. Т.е. страничка с пустым body и подключенными скриптами и стилем.
Т.е. как вы видите, в отличии от некоторых современных javascript mvc framework проект не требует особого подготовления, поэтому уже существующий проект может быть “переписан” на backbone.

Шаг 1. Начальная вёрстка (rev 1)



Наша страница, в соответствии с задачей, должна иметь 3 состояния: ввод имени пользователя, состояние при удачном сравнении, состояние при неудачном сравнении. Для начала сверстаем 3 дива, каждому состоянию по своему месту.

<div id="start" class="block">
	<div class="userplace">
	    <label for="username">Имя пользователя: </label>
	    <input type="text" id="username" />
	</div>
	<div class="buttonplace">
	    <input type="button" value="Проверить" />
	</div>
</div>
<div id="error" class="block">
	Ошибка такой пользователь не найден.
</div>
<div id="success" class="block">
	Пользователь найден.
</div>


Блоки #error и #success скроем от глаз подальше при помощи CSS.

#error, #success
{
    ...
    display: none;
}


На этом шаге мы полностью подготовили всё для внедрения backbone. Эти шаги идентичные для многих реализаций одностраничных сайтов.

Шаг 2. Внедряем Router (rev 2)



Раньше до 0.5.0 этот класс звали Controller. Его назначение обработка хеш навигации в приложении. Т.е. он никогда не был в полном понимании контроллером, просто хеш навигация это контроллер приложения. Видно логика разработчиков взяла верх и теперь мы имеем класс Router.
Что такое location.hash для чего он используется, и как его использовать правильно вы можете прочитать на хабре (тут, тут или тут).

Для начала, на время создадим импровизированное меню в index.html
<div id="menu"> <!-- Блок меню -->
    <ul>
        <li><a href="#!/">Start</a></li>
        <li><a href="#!/success">Success</a></li>
        <li><a href="#!/error">Error</a></li>
    </ul>
</div>


А потом легким движением руки добавляем работу роутинга в пример:
var Controller = Backbone.Router.extend({
    routes: {
        "": "start", // Пустой hash-тэг
        "!/": "start", // Начальная страница
        "!/success": "success", // Блок удачи
        "!/error": "error" // Блок ошибки
    },

    start: function () {
        $(".block").hide(); // Прячем все блоки
        $("#start").show(); // Показываем нужный
    },

    success: function () {
        $(".block").hide();
        $("#success").show();
    },

    error: function () {
        $(".block").hide();
        $("#error").show();
    }
});

var controller = new Controller(); // Создаём контроллер

Backbone.history.start();  // Запускаем HTML5 History push    


Вот таким простым кодом мы создали простейший tab-орентированный сайт с возможностью делать закладки на страницы.

Шаг 3. Простейшее View (rev 3)


View в backbone это смесь контроллера и View из стандартной MVC модели. Можно сказать проще, View тут это widget/component на странице, который умеет себя отображать, реагировать на события и создавать события. Будем наедятся, что создатели задумаются и переименуют View в Widget (или component), как ранее они сделали с Router.
У нас есть сформировавшийся widget проверки имени пользователя, это блок start. Давайте сделаем так, что если введено имя “test”, то перейдём на хеш тег #!/success, который покажет блок success. А если введено что-то иное, то перейдём на хеш тег #!/error, который покажет, соответственно, блок error. Кстати, заодним уберем меню, оно нам больше не понадобится.

var Start = Backbone.View.extend({
    el: $("#start"), // DOM элемент widget'а
    events: {
        "click input:button": "check" // Обработчик клика на кнопке "Проверить"
    },
    check: function () {
        if (this.el.find("input:text").val() == "test") // Проверка текста
            controller.navigate("success", true); // переход на страницу success
        else
            controller.navigate("error", true); // переход на страницу error
    }
});

var start = new Start();


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

Ремарка. JQuery way (rev 4)


Вы заметили сколько кода мы уже написали? Я так и думаю, многие уже из тех кто дочитал до этого предложения получат коньяк воскликнули: “На jQuery это делается быстрее и проще”. Не спорю. Код который надо написать на начальную вёрстку очень прост:
    $("#start input:button").click(function () { // Обработчик нажатия кнопки
        var username = $("#username").val(); // Получаем значение введенное пользователем
        $("#start").hide(); // Скрываем основной экран
        if (username == "test")
            $("#success").show(); // Показываем успех
        else
            $("#error").show(); // Показываем ошибку
    });

Но… Данный код не поддерживает хеш навигацию, плохо расширяется и очень плохо поддерживается.
Не для кого не секрет, что программист в своей работе занимается созданием новых приложений всего 20% времени. 80% же своего времени он состыкует модули, занимается исправлением ошибок и расширяет функционал уже созданных проектов. А поддержка jQuery лапши может очень дорого стоить. Очевидный способ избежать геморроя на пальцах, это заняться декомпозицией проектов, для чего в основном изобретают велосипеды. Backbone уже готовый велосипед. Зачем придумывать что то новое, когда за вас это сделал добрый дядя?

Шаг 4. Работа со View через Template (rev 5)


View было бы не View, а контролером, если бы не умела себя отображать. В backbone нет своего механизма для этого. Смешно? Нисколько… Его назначение не давать инструмент для создания приложения, а дать шаблон, используя который можно было бы создать максимально поддерживаемую систему. Поэтому в backbone можно использовать различные template движки. Например, встроенный в underscore.js движок от John Resig. Или подключить Microsoft Template. А если хитрожопно извернуться то можно реализовать всё через Knockout.js (хотя меня напрягает его свалка логики и шаблонов)
Мы не будем напрягаться и просто используем _.template из underscore.js для реализации своих идей. Для этого создадим один пустой блок на странице, а все “наполнители” вынесем в шаблоны. Соответственно изменятся и стили страницы.
<div id="block" class="block">
</div>

<!-- Блок ввода имени пользователя -->
<script type="text/template" id="start"> 
    <div class="start"> 
        <div class="userplace">
            <label for="username">Имя пользователя: </label>
            <input type="text" id="username" />
        </div>
        <div class="buttonplace">
            <input type="button" value="Проверить" />
        </div>
    </div>
</script>

<!-- Блок ошибки -->
<script type="text/template" id="error">
    <div class="error">
        Ошибка. Пользователь  <%= username %> не найден.
        <a href="#!/">Go back</a>
    </div>
</script>

<!-- Блок удачи -->
<script type="text/template" id="success">
    <div class="success">
        Пользователь <%= username %> найден.
        <a href="#!/">Go back</a>
    </div>        
</script>


Для того чтобы показать динамику, мы добавили в шаблоны результатов имя пользователя.
Хранить имя пользователя и передавать его в шаблон мы будем в переменной AppState
var AppState = {
    username: ""
}


Напишем View для каждого шаблона.
var Views = { };    

var Start = Backbone.View.extend({
    el: $("#block"), // DOM элемент widget'а

    template: _.template($('#start').html()),

    events: {
        "click input:button": "check" // Обработчик клика на кнопке "Проверить"
    },

    check: function () {
        AppState.username = this.el.find("input:text").val(); // Сохранение имени пользователя
        if (AppState.username == "test") // Проверка имени пользователя
            controller.navigate("success", true); // переход на страницу success
        else
            controller.navigate("error", true); // переход на страницу error
    },

    render: function () {
        $(this.el).html(this.template());
    }
});

var Success = Backbone.View.extend({
    el: $("#block"), // DOM элемент widget'а

    template: _.template($('#success').html()),

    render: function () {
        $(this.el).html(this.template(AppState));
    }
});

var Error = Backbone.View.extend({
    el: $("#block"), // DOM элемент widget'а

    template: _.template($('#error').html()),

    render: function () {
        $(this.el).html(this.template(AppState));
    }
});

Views = { 
            start: new Start(),
            success: new Success(),
            error: new Error()
        };

Замечание. У нас 3 View сылаются на один и тот же DOM элемент. В реальности такого быть не должно. Логически, это должен быть один widget. Я сознательно неверно спроектировал данный шаг, для того чтобы показать возможность работы нескольких View. Позднее я покажу как избежать данный прокол.

Контроллер тоже претерпит небольшие изменения
var Controller = Backbone.Router.extend({
    routes: {
        "": "start", // Пустой hash-тэг
        "!/": "start", // Начальная страница
        "!/success": "success", // Блок удачи
        "!/error": "error" // Блок ошибки
    },

    start: function () {
        if (Views.start != null) {
            Views.start.render();
        }
    },

    success: function () {
        if (Views.success != null) {
            Views.success.render();
        }
    },

    error: function () {
        if (Views.error != null) {
            Views.error.render();
        }
    }
});

Вот таким образом мы добавили динамики к нашему приложению.

Шаг 5. Проверка на несколько пользователей (rev 6)


Самым простым способом проверки не только на test, но и на других пользователей, это проверка на нахождения имени в массиве пользователей.
Создадим массив Family, в который и забьем все имена.
var Family = ["Саша", "Юля", "Елизар"]; // Моя семья

А проверку сделаем в коде вьюшки Start. Т.к. underscore уже включено в приложение, сделаем через _.detect
...
if (_.detect(Family, function (elem) { return elem == AppState.username })) // Проверка имени пользователя
...

Какие проблемы есть у данного решения? Основная проблема в том, что если завтра нам нужно будет сменить физическое расположение массива пользователей (сервер, localstore и т.д.), то нам придётся менять логику работы View. Т.е. View настолько завязана на метод доступа к данным, что придётся менять его код при малейшем чихе.

Ремарка 2. jQuery way. Продолжение (rev 7)


А намного ли кода нам пришлось добавить в jQuery код чтобы поддерживать нескольких пользователей? Очень мало:
$(function () {

    var Family = ["Саша", "Юля", "Елизар"];

    $("#start input:button").click(function () { // Обработчик нажатия кнопки
        var username = $("#username").val(); // Получаем значение введенное пользователем
        $("span.username").text(username);
        $("#start").hide(); // Скрываем основной экран
        $("#" + ($.inArray(username, Family) != -1 ? "success" : "error")).show();
    });
    
    $("#error a, #success a").click(function () {
        $(".block").hide();
        $("#start").show();
    });

});

Ну и соответственно подправить вёрстку макета:
        ...        
        <div id="error" class="block"> <!-- Блок ошибки -->
            Ошибка. Пользователь <span class="username"></span> не найден.
            <a href="javascript:void(0);">Go back</a>
        </div>
        <div id="success" class="block"> <!-- Блок удачи -->
            Пользователь <span class="username"></span> найден.
            <a href="javascript:void(0);">Go back</a>
        </div>
        ...

Помните я говорил, что то про поддержку, 80/20% и прочую муть? Так вот. Забудьте. Для данного приложения нет ничего постыдного написать код в стиле jQuery way. Вы потратите времени в 10-20 раз меньше, чем писать это всё через Backbone. А размеры кода позволяют поддерживать это приложение хоть ночью после пол-литры. Нет ничего постыдного писать таким способом и зарабатывать свои $5. Кто не согласен, пусть засунет своё мнение в комментарий.
Я люблю повторять фразу, что все framework’и служат 2 целям, делать из миллиардного проекта, проект на миллион, и из проекта за $100 — проект на пару миллионов. Пользуетесь тем что эффективнее сэкономит ваше время и деньги.

Шаг 6. Контроллер приложения через Модель (rev 8)


Замечательная функция Backbone это возможность связать модель и представление. Если создать представление с параметром model, то в методе initialize представления можно подписаться на возникновение события изменения модели. После чего, View будет получать сообщения, при каждом изменении модели либо ее части. И уже на это сообщение привязать определенное поведение предоставления, например полную либо частичную его перерисовку.
Помните я говорил, что некрасиво, когда один и тот же блок обрабатывается несколькими View. Попробуем отвязаться от этого засилья обработчиков.
Для начала, из объекта AppState создадим модель, которая будет содержать имя пользователя и состояние приложения:
var AppState = Backbone.Model.extend({
    defaults: {
        username: "",
        state: "start"
    }
});

var appState = new AppState();

Вторым шагом, удалим вьюшки Success и Error, а view Start переименуем в Block, т.к. она будет обрабатывать несколько состояний, а не только стартовое. Во оставшимся view переименуем поле template в templates в котором будут храниться все шаблоны для различных состояний
    var Block = Backbone.View.extend({

        templates: { // Шаблоны на разное состояние
            "start": _.template($('#start').html()),
            "success": _.template($('#success').html()),
            "error": _.template($('#error').html())
        },

В инициализаторе представления подпишемся на событие изменения модели. На данное событие повесим перерисовку блока.
        initialize: function () { // Подписка на событие модели
            this.model.bind('change', this.render, this);
        },

Функция перерисовки (render) будет “отрисовывать” нашу главную модель соответствующим шаблоном, зависящим от поля state модели:
        render: function () {
            var state = this.model.get("state");
            $(this.el).html(this.templates[state](this.model.toJSON()));
            return this;
        }

Изменится также функция check. Она будет устанавливать соответствующие поля модели:
        check: function () {
            var username = this.el.find("input:text").val();
            var find = (_.detect(Family, function (elem) { return elem == username })); // Проверка имени пользователя
            appState.set({ // Сохранение имени пользователя и состояния
                "state": find ? "success" : "error",
                "username": username
            }); 
        },

Кстати, после всех этих дел у нас ничего не отобразится, т.к. модель была создана до того как мы описали View. Поэтому возбудим событие change уже после того как мы создадим View:
    var block = new Block({ model: appState });
    appState.trigger("change");


Если бы у нас не было бы хеш навигации, я бы закончил этот шаг. Но у нас полетела навигация. Восстановим её. Для этого перепишим код роутера.
    var Controller = Backbone.Router.extend({
        routes: {
            "": "start", // Пустой hash-тэг
            "!/": "start", // Начальная страница
            "!/success": "success", // Блок удачи
            "!/error": "error" // Блок ошибки
        },

        start: function () {
            appState.set({ state: "start" });
        },

        success: function () {
            appState.set({ state: "success" });
        },

        error: function () {
            appState.set({ state: "error" });
        }
    });

Ручная навигация заработала, но при нажатии на кнопку Проверить не меняется хеш адреса страницы. Исправим этот досадный недостаток при помощи подписки на событие изменения поля state у модели:
    appState.bind("change:state", function () { // подписка на смену состояния для контроллера
        var state = this.get("state");
        if (state == "start")
            controller.navigate("!/", false); // false потому, что нам не надо 
                                              // вызывать обработчик у Router
        else
            controller.navigate("!/" + state, false);
    });

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

Вот и всё с самым большим рефакторингом в этом маленьком проекте. И на будущее, начинайте проектирование системы с моделей, а не с View как это сделал я и ваши волосы будут, просто будут.

Шаг 7. Проверка на несколько пользователей через коллекцию (rev 9)


То что мы реализовали на 5 шаге имеет свой недостаток. Мы смешали логику отображения с логикой управления данными. Мы не сможем сейчас просто, не перестраивая логику работы View, заменить наш массив на обращение к сервису. Именно для этих целей в Backbone используются коллекции.
Коллекция в данном framework’е это сортированный набор моделей, который умеет обращаться с этими моделями, фильтровать или сортировать их. Также коллекции умеют из коробки работать с сервисами по REST интерфейсу. Фактически это прослойка между widget’ом и способами доступа к базе данных.
Вернёмся от рассуждений к нашей задаче. Создадим модель UserNameModel. Единственным обязательным полем данной модели будет поле Name, которое по умолчанию имеет пустое значение.
    var UserNameModel = Backbone.Model.extend({ // Модель пользователя
        defaults: {
            "Name": ""
        }
    });

Создадим коллекцию Family из моделей UserNameModel
    var Family = Backbone.Collection.extend({ // Коллекция пользователей
        model: UserNameModel,
    });

Добавим в коллекцию метод проверки нахождения пользователя с указанным именем в данной коллекции
        checkUser: function (username) { // Проверка пользователя
            var findResult = this.find(function (user) { return user.get("Name") == username })
            return findResult != null;
        }

Создадим экземпляр коллекции Family
    var MyFamily = new Family([ // Моя семья
                { Name: "Саша" },
                { Name: "Юля" },
                { Name: "Елизар" }
            ]);

После чего проверка пользователя во View сокращается до вызова метода проверки в экземпляре MyFamily
var find = MyFamily.checkUser(username); // Проверка имени пользователя


Вывод


В процессе статьи мы создали учебный проект, который ни фига не делает, но который не мой взгляд всесторонне охватывает данный framework. В результирующем файле примерно 200 строк кода. Это больше чем в варианте с jQuery, но это хорошие легко расширяемые строки. Объекты знают о друг друге необходимый минимум и не больше того.
Backbone на удивление оказался хорошим продуктом позволяющим построить хребет для своего сервиса. Он даёт платформу для создания одностраничных сервисов и различных крупных динамических приложений. Как уже показано в ремарках, использовать его иногда, на маленьких проектах, бывает невыгодно. Но как только мы разрастаемся, и сложность поддержки нашего приложение растёт по экспоненте, то используя backbone можно значительно сократить трудозатраты на поддержку, оставляя время для наработку нового функционала.
Коротаев Александр @aavezel
карма
48,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (51)

  • –1
    у peepcode есть 2 части скринкастов о backbone.js. Там подробнее рассматривают некоторые аспекты + порядок разработки совсем другой
    • +1
      По поводу полноты: Статья для чайников. Можно углубится в модели или коллекции, которые тут почти не рассмотрены. Можно по шаблоны поговорить. Можно о underscore.js поговорить. Да много что можно. Я сознательно это выкинул, т.к. пишу статью, а не книгу. Мне кажется, что статья и так огромная, я бы половину выкинул, но…

      По поводу порядка:
      И на будущее, начинайте проектирование системы с моделей, а не с View как это сделал я и...
      Вот, например, хороший пример как надо писать на bb, но там нет разделения по полочкам, которого мне не хватило…
      • 0
        в скринкастах peepcode как раз начинают с моделей.

        Зачем учить «чайников» плохому проектированию?
        • +1
          Зачем учить ребёнка ползать, если он всё равно ходить на ногах будет?
          • –4
            те ваша статья для дизайнеров, которые не знают основ javascript и садятся за бекбоун?
            • +9
              Смотри, моя статья строится так чтобы каждый элемент в BB вводить отдельно. Т.е. я вначале описал работу роутера, потом view, потом показал что view должно работать с шаблонами, потом показал что такое модель и события, потом что такое коллекция. Всё это показал отдельно. Каждый шаг показывает что-то из BB в разрезе и работает отдельно.

              Если начинать показывать с модели, то мы сделаем практически наоборот. Сначала зачемто опишем модель, потом опишем коллекцию, потом покажем что есть view, потом всё соберём вместе с роутером. И только после этого мы можем что нибудь посмотреть. Т.е. пока мы это не собрали мы не видим результата.

              Именно поэтому я считаю, что учить вторым способом учить «чайники» неэффективно. Если сравнивать с ребёнком, то это всё равно что сначала объяснить ему что для того чтобы открывать дверь, перед этим надо уметь ходить, а для этого надо стоять. Что бы встать надо уметь сидеть, а для того чтобы сидеть нужно держать голову. Мы не делаем всё это последовательно, не объясняем каждый шаг, не закрепляем успехами. Мы не объясняем что для того чтобы передвинутся нам не надо стоять, можно и ползать, но на двух ногах ходить эффективнее.
              • –1
                Мне понравилась ваша статья по стилю изложения. По-шаговое добавление функциона, усложнение и рефакторинг — это классно. Однако, совсем не вписывается в концепцию tdd/bdd.
                Аналогия с детьми мне кажется неуместно.
                Эту статью будут читать вебдевелоперы, которые обладают определёнными знаниями в js, а не те, кто учится «ходить».

                Вообще говоря написав модель и вью можно посмотреть результат — через консоль браузера рендерить элементы.

                Вы, случайно, не будете выступать на javascript meetup?
                • +5
                  Однако, совсем не вписывается в концепцию tdd/bdd

                  Может потому, что я не вписывался в эту концепцию? ;-)

                  Эту статью будут читать вебдевелоперы, которые обладают определёнными знаниями в js, а не те, кто учится «ходить».

                  Эта статья для тех кто уже обладает определёнными навыками работы с JS и даже знает jQuery, но только-только дорос до использования других framework-ов в своих проектах. «Чайников» одним словом.
                  Мы все, до старости учимся «ходить», сначала ногами, потом головой, потом по деффкам, потом по инстанциям и т.д.

                  Вы, случайно, не будете выступать на javascript meetup?

                  Нет. Слишком узкоизвесный я джаваскриптист :) Вот перееду в дефаулт сити, устроюсь в яндекс, а там может быть и выступлю на js meetup
                • 0
                  Вот я веб-девелопер-самоучка. Университетов не кончали, учусь на своих ошибках.
                  Среди последних проектов два были сайтами, по функциональности близкими к веб-приложениям. Там и навигация через хэши, и обработка тучи событий. И всё это писалось на jquery. Получилось оба раза крепко за тысячу строк. Меня ужас берёт при одной мысли о том, что заказчик вдруг захочет там что-то изменить.
                  И, знаете ли, быдлокодить как-то не очень приятно, хочется расти. Эта статья очень помогла. Сегодня, правда, буду заниматься совершенно жутким занятием: буду писать банальные вкладки с использованием backbone.js. Это тоже не комильфо, не так ли? Простите, но надо учиться.
                  А автору статьи большое спасибо.
  • 0
    Может быть вы подскажете, как понять что пора заменить JQuery-код на, например backbone?
    Например, я пишу админку для проекта, там ajax-CRUD, но на одностраничный сервис это явно не тянет.
    Стоит и там использовать bb?
    • +2
      Да стоит, но Backbone это не никак не замена jQuery, это совсем другое.
      • 0
        Я не ищу замены, я бы хотел уйти от кучи js-файлов для каждой страницы и как-то все это дело организовать, но как — пока не нашел решения)
        • +1
          Недавно открыл для себя sprockets. Думаю аналогичные решения должны быть и для других языков.
          • 0
            меня руби более чем устраивает, почитаю подробнее, спасибо!
        • 0
          Google Closure Tools поможет структурировать код и скомпилировать его в один файл. К тому же на Google Closure Library можно написать свое MVC приложение, и для этого не нужен Backbone/Underscore/jQuery. Правда Google Closure сложный фреймворк для начинающих (или только знающих jQuery) программистов.
          • +1
            Ну проблема-то не в том чтобы скомпилировать, а в том чтобы как-то организовать кучу ивентов, запросов и ответов, чтобы это можно было потом разобратЬ)
            • 0
              В BB это делается при помощи событий. Для javascript есть решение openajax hub.
            • 0
              >организовать кучу ивентов, запросов и ответов, чтобы это можно было потом разобратЬ)
              стоит посмотреть в сторону YUI3
    • +1
      Признаков внедрять backbone на мой взгляд два: Во первых, если вы не можете с налёту сказать что это за обработчик. Во вторых, если ajax запросов столько, что вы не можете с налёту сказать в каком месте у вас находится обращение к этому сервису и что оно там делает.
      В реальности же, сядьте и за 3-4 часа напишите пятнашки (морской бой, крестики нолики), после этого вы будете понимать, надо ли тут использовать тяжелую технику или нет.
  • 0
    Вчера как раз полдня ловили ошибку в ие, которую с вашим кодом теоретически тоже можно словить:

    var Block = Backbone.View.extend({

    templates: { // Шаблоны на разное состояние
    "start": _.template($('#start').html()),
    "success": _.template($('#success').html()),
    "error": _.template($('#error').html())
    },

    В данном случае templates инициализируются при создании констуктора view, а не экземпляра. И нет гарантии, что в этот момент нужные элементы с шаблонами будут в DOM. Чтобы избежать этого, стоит вынести создание шаблонов в метод initialize, который вызывается каждый раз при создании экземпляра:

    var Block = Backbone.View.extend({

    initalize: {
    this.templates = {
    "start": _.template ($('#start').html()),
    "success": _.template($('#success').html()),
    "error": _.template($('#error').html())
    }
    },

    По-хорошему, конечно, нужно добавить кэширование, чтобы шаблоны не компилировались каждый раз.
    • 0
      Есть. Называется обработка document ready при помощи jQuery ($(function(){… })) Ваш код тоже не совершенен в данном случае, т.к. экземпляр создается тут же после создания конструктора.
      По поводу кеширования, _.template создает функцию-шаблон. Там уже всё что надо закешировалось… Или я не понял вас?
      • 0
        да, насчёт $(document).ready вы правы, но в вашем случае необходимо чтобы определение view находилось внутри ready, а в моём — создание экземпляра. Что мне показалось более логичным, по крайней мере, у нас в коде экземпляры вьюх создаются только внутри ready.

        Про кэширование я имел ввиду, что в моём случае _.template будет выполняться три раза (для каждого шаблона) каждый раз при создании view (то есть вызове new Block(...)). Чтобы этого избежать, скомпилированную функцию-шаблон стоит где-нибудь сохранять.
        • 0
          Придерживаюсь правила, что почти все javascript операции должны находиться внутри ready. Пользователь не должен ждать пока там что-то проинициализируется. Если есть процесс долгой инициализации, то надо делать как gmail — progress bar.

          По поводу кеширования, попробуйте Microsoft Template у них есть кеширования из коробки.
          • 0
            по поводу инициализации, здесь смотря что считать долгой. скажем, полсекунды мало для progress bar-a а ля gmail, но уже ощутимо для пользователя. Если всю инициализацию выполнять после ready, есть риск заметно замедлить инициализацию приложения. Но в общем, чтобы не холиварить — мне кажется, стоит указать в вашем тексте, или в комментариях к коду, что предполагается, что view будет определяться после document ready.

            У jQuery Templates, на которые вы указали, кэширование есть, но оно не совсем из коробки. То есть насколько я понимаю, многократный вызов $('#comment').template() будет-таки каждый раз компилировать шаблон, так что нужно руками проверять перед его вызовом, есть ли шаблон в $.template, и если да, то возвращать его.
            • 0
              Есть риск существенно замедлить рендринг страницы, если очень много кода будет исполняться при загрузке самого скрипта. Помните про законы вёрстки быстрых страниц: стили в head, скрипты после body? Помните причину?..

              Не совсем.
  • 0
    Как насчёт тестирования Backbone?
  • 0
    Имеет ли смысл использовать Backbone если на сайте не нужно изменение урла при переключении окон, т.е. без Router?
    • +1
      Скажем так, если переформулировать вопрос как «следует ли отказываться от Backbone если не будет использоваться Router?», то ответ однозначно нет. Router не такая большая часть Backbone, гораздо больше (и, имхо, гораздо полезнее) логика работы моделей, коллекций, и связывание view с данными через систему событий.
    • +1
      И если переформулировать так — «имеет ли смысл использовать Backbone только ради Router» — ответ однозначно нет. Вы можете воспользоваться, например, plugins.jquery.com/project/router

      Понимаю, что спрашивали не об этом — но может пригодиться кому-то.
  • 0
    Хорошая статья. Было бы неплохо видеть конечный результат в работе. Есть такой?
    • 0
      Все шаги сохраним через mercurial. Поэтому, читая какой либо шаг вы можете распаковать zip архив (+ dropbox, если на народе удалят), зайти в каталог и перейти на нужную ревизию при помощи команды
      hg update --rev <номер ревизии>
      После чего посмотреть на код и понять то, что вам не понятно :)
  • 0
    Такая активность в последнее время по поводу MVC на клиенте это сейчас такой модный тренд или реально пришла пора? :)
    • 0
      MVC слишком абстрактная штука, по сути, поэтому она везде разная)
  • 0
    Данный код не поддерживает хеш навигацию, плохо расширяется и очень плохо поддерживается.

    вариант rev 7 с поддержкой хеш навигации (используя benalman.com/projects/jquery-bbq-plugin/):

    $(function () {
        var Family = {
          _members: ["Саша", "Юля", "Елизар"]
          , hasMember: function(username) { return $.inArray(username, this._members) != -1; }
        }
    
        $("#start input[type=button]").click(function () { // Обработчик нажатия кнопки
    	var username = $("#username").val(); // Получаем значение введенное пользователем
    	$("span.username").text(username);
    	$.bbq.pushState('#'+(Family.hasMember(username)? 'success' : 'error'));
        });
    
        $(window).bind('hashchange', function(){
    	    $('.block').hide();
    	    $('#'+($.param.fragment() || 'start')).show();
        }).trigger('hashchange');
    });
    
    • 0
      Но код все еще «плохо расширяется и очень плохо поддерживается»
  • НЛО прилетело и опубликовало эту надпись здесь
    • 0
      Да, было бы интересно почитать как работать с ассоциациями.
  • +1
    Супер! Я бы посоветовал автору статьи предложить Backbone эту статью в качестве оффициального first-steps-tutorial
    • +1
      Возьмитесь перевести на чистый английский? :)
      • +1
        Я бы с удовольствием, но мои познания в английском, боюсь, до нужного уровня не дотягивают — скорее всего в результате получится «моя твоя не понимай», только на английском. А жаль.
        • +1
          Как я вас понимаю ;-)
  • 0
    Автору статьи огромное спасибо. Несколько раз уже пробовал разобраться с bb, но всё никак не удавалось.
  • 0
    но это хорошие легко расширяемые строки

    … хорошие демократические легко расширяемые строки =)
  • 0
    А где можно подробнее узнать о связи с сервером — ну как данные туда обратно ходят
  • 0
    на Code School вышло уже две части курса по backbonejs (1, 2)
  • НЛО прилетело и опубликовало эту надпись здесь
  • 0
    Как View шаблоны по файлам раскидать, как в ангуляре?

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