Backbone.js и routes без хэшей

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

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

<a href="http://myapp.com/catalog/#action1">Сделать что-то полезное</a>

или даже

<a href="http://myapp.com/catalog/#action1/42">Сделать что-то полезное с параметром</a>

и всё прекрасно работало. Пользователь, в свою очередь, мог сохранить каждую из этих ссылок, и, в последствии, перейти по ним, сразу же активировав нужную функциональность. Единственный негативный момент во всём этом великолепии — наличие символа "#" (решетка). Не то, чтобы она была плоха сама по себе, но читабельность и «красивость» (с задних рядов мне ещё кто-то про RESTful что-то подсказывает) URL она портила.

До пришествия html5 приходилось с этим мириться. С появлением же нового html появилась возможность управлять историей переходов браузера (а вместе с ней и внешим видом URL в адресной строке). Соответствующая функциональность появилась и в backbone (естественно, fallback для старых браузеров поддерживается). Попробуем ею воспользоваться, чтобы наше приложение выглядело модно и современно.

Документация (и даже StackOverflow) по этому поводу нам говорит просто: «Хотите роутинга без хэшей? Делайте при старте приложения так:»

Backbone.history.start({pushState: true})

Конечно же, как только мы добавлем к своему существующему коду определение параметра pushState, все идёт, так как ожидается. Т.е. ничего не работает. Происходит это из-за двух проблем:
  1. теперь приложение не знает, какая часть URL является собствено адресом, а какая — именем backbone-контроллера (поэтому по умолчанию считает этим именем всё после имени домена);
  2. если в href у ссылки указан URL без хэша, браузер не будет разбираться с тем, что мы там именно хотели, а просто перейдёт по указанному адресу (что для нашего приложения означает в лучшем случае полный рестарт).

Первую проблему можно решить относительно просто. У метода start имеется помимо параметра pushState ещё парочка параметров. Нам особено интересен root. Для нашего примера в начале текста запуск роутера будет выглядеть так:

Backbone.history.start({pushState: true, root: "/catalog"})

Теперь можно смело писать:

<a href="http://myapp.com/catalog/action1/42">Сделать что-то полезное с параметром</a>

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

<a onclick="Router.navigate("http://myapp.com/catalog/action1/42", {trigger: true} )" href="javascript:">Сделать что-то полезное с параметром</a>

Т.е. теперь мы принудительно обновляем состояние приложения при каждом клике. Надо не забыть установить параметр trigger чтобы наш роутер всё-таки вызвался (а не просто сменился URL в адресной строке).

Остаётся предоставить пользователю возможность скопировать ссылку. Для этого сделаем простую функцию-обработчик ссылок, которую будем вызывать перед history.start. У меня эта функция вешает обработчик onclick для всех ссылок с id=«backbone» (но я не настаиваю на единственности такого подхода):

<a id="barebone" href="/catalog/action1/42">Сделать что-то полезное с параметром</a>

Не забываем, что в href указывается всё-таки URL, а не routes (хотя меня лично подмывает написать просто «action1/42»). Ну и сама функция (используется jQuery, что совсем не обязательно):

var fRouterLinks = function()
{
$("#barebone").click(function(){
Router.navigate($(this).attr("href"), {trigger: true, replace: true} );
return false;
});
}

Всё. Наслаждаемся красивыми URL.

Как видим, тьюториал получился вполне элементарным, но именно разрозненность информации по этому вопросу и заставила меня его написать.

Комментарии 17

    +10
    id=backbone?
    id должен быть уникальным на странице =/
      0
      В остальном очень даже интересно
        +3
        Совершенно справедливо. Надо использовать data-*
        0
        А что там подсказывают с задних рядов по поводу RESTful для данной темы?
          0
          Значение после хэша не передается на сервер.
            0
            Так мы вроде говорим сугубо про технологию работающую на клиенте?
              0
              Пользователь, в свою очередь, мог сохранить каждую из этих ссылок, и, в последствии, перейти по ним, сразу же активировав нужную функциональность.

                0
                Ладно, я наверное не до конца понимаю что такое RESTful
          0
          А что будет, если перейти на /catalog/action1/42 и нажать в браузере F5? (.htaccess допустим не работает)
            0
            Ну так backbone без бэкенда смысл использовать?
              0
              Rewrite, конечно же, надо настраивать. Иначе будет 404. Я лично использую Yii, и просто добавляю несколько правил в urlManager.
              0
              А вот Вам такой вопрос: что должен отдавать сервер при запросе /catalog/action1/42? Пустой шаблон приложения (который подтянет AJAX-ом данные с сервера), или же реальную сгенерированную страницу (и как тогда сообщить Backbone-у что тягать с сервера для этой страницы уже не надо).
                0
                Если вкратце, то я реализовал первый вариант. Т.е. фактически загружается приложение (/catalog), которое тут же выполняет указанное действие (/action1/42).

                Во втором случае просто сгенерированной страницей не обойтись — всё равно нужно загружать все необходимые модели etc, т.к. приложение у нас должно работать одинаково вне зависимости от точки входа.
                  0
                  Получается мы плюём на пользователей с отключенным JS?
                  0
                  Для второго случая.
                  А кто мешает предусмотреть передачу в теле страницы JSON-моделей и инициализировать на их основе объекты?

                  Или даже инициализировать модели через обход DOM полученной страницы?
                    0
                    Кроме лени, подкреплённой статистикой — ничего.

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

              Самое читаемое