Тонкости Rails 4 — Turbolinks

Original author: Ryan Bates
  • Translation
  • Tutorial


Гем под названием Turbolinks способен неплохо ускорить ваше приложение, используя JavaScript для обновления контента на странице. Он включен по умолчанию в Rails 4, но я покажу, как можно использовать его уже сейчас в программах, написанных на Rails 3. Также вы узнаете о некоторых подводных камнях.



Cодержание цикла «Тонкости Rails 4»



Turbolinks позволяет ускорить практически любое Rails-приложение, поскольку загружает только тело страницы. Да, делается это все добро с помощью JavaScript. В этой статье мы опробуем работу гема на приложении, написанном на третьих рельсах.

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



Добавление Turbolinks

Теперь добавим Turbolinks в наше приложение и посмотрим, как все это работает. В первую очередь необходимо вписать имя гема в gemfile и затем запустить bundle install.

/Gemfile
gem 'turbolinks'

Теперь необходимо добавить в Javascript манифест приложения строчку для добавления turbolinks:

/app/assets/javascripts/application.js
//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require_tree .

К слову, гем не требует наличия JQuery, что в некоторых случаях может быть очень даже удобно.

Перегрузив после этого сервер рельс и полазив немного по приложению возможно вы не увидите особой разницы. Чтобы проверить, а работает ли собственно Turbolinks необходимо открыть network inspector браузера и затем уже побродить по сайту. В моем случае, после описанных выше шагов я увидел, что при переходе на какую-либо страницу она загружается целиком, таким образом вывод: гем не заработал. Это может происходить в том случае, если ваш браузер не поддерживается Turbolink’ом, в таком случае стоит его сменить (и, желательно, на что-то известное), либо обновиться (если это возможно). Гем ожидает, что используется наиболее свежая версия одного из популярных браузеров. Иначе он просто не будет работать, но при этом приложение будет функционировать нормально и без него.



В моем случае я поменял мой браузер на новую версию Chrome и открыл в нем разрабатываемый сайт. Запустив веб-инспектор и покликав по ссылкам будет видно, что страница не перегружается полностью, поскольку теперь гем работает и turbolinks.js уже генерирует ajax-запрос для открываемой страницы.



Использование гема позволяет сайту загружаться быстрее, поскольку браузер не занимается заново интерпретацией JS и CSS кода, но как же все же это работает?

Turbolinks для всех ссылок на странице слушает событие click. Как только оно происходит, к нужной странице делается GET-запрос через JavaScript и после этого анализируются полученные данные, а затем обновляются элементы title и body. Гем также использует Push State API для изменения URL обновленной страницы. Подобная техника очень похожа на PJAX.



Проблемы с уже существующими скриптами

Написанное для этого урока приложение, казалось бы, работает с Turbolinks довольно неплохо, но тем не менее, даже в нем возникают проблемы с существующими скриптами на JavaScript. Увидеть эту проблему очень просто, стоит всего лишь попытаться переключить значение в чекбоксе задачи. Ничего не произойдет. Вообще. Однако, если вручную обновить страницу, то проблема волшебным образом испарится.


Собственно, вот код, с которым возникают проблемы:

/app/assets/javascripts/projects.js.coffee
jQuery ->
  $('.edit_task input[type=checkbox]').click ->
    $(this).parent('form').submit()

Код ожидает событие click в одном из чекбоксов задач. Как только оно произошло, форма с чекбоксом отправляется на сервер, после чего задача помечается как завершенная.

Приглядитесь к первой строке кода. Она очень важна, поскольку именно в ней происходит ожидание события ready для страницы, срабатывающий только после того, как DOM страницы целиком загрузился. Поэтому, если не использовать этот код, то jQuery попытается приаттачить event'ы к чекбоксам еще до их загрузки.

При использовании Turbolinks этот callback исполняется только один раз, при загрузке страницы. Это подводит нас к другой важной мысли: при переходе на другую страницу, благодаря вышеописанным особенностям гема чисто технически мы остаемся на той же самой странице. Однако сам Turbolinks генерирует несколько различных событий при загрузке новой странице и один из них — page:load. Это позволяет эмулировать загрузку DOM:

/app/assets/javascripts/projects.js.coffee
ready = ->
  $('.edit_task input[type=checkbox]').click ->
    $(this).parent('form').submit()

$(document).ready(ready)
$(document).on('page:load', ready)

Теперь можно определить переменную ready, сохранить в ней нашу многострадальную функцию, после чего передать переменную в document.ready и page:load. Таким образом, события теперь прикреплены к чекбоксам в независимости от того, используется Turbolinks или нет. Теперь функциональность восстановлена: при проставлении галочки в чекбоксе задачи она будет автоматически помечена как завершенная. И также легко оконченную задачу можно превратить в незавершенную.

Для решения похожих проблем можно воспользоваться Jquery Turbolinks gem. Кроме того, существует еще одно решение для обхода подобных подводных камней. Вместо того, чтобы находить элементы и отслеживать их click события можно слушать событие click для всего документа и при его возникновении проверять, что оно было вызвано для чекбокса с задачей:

/app/assets/javascripts/projects.js.coffee
$('document').on 'click', '.edit_task input[type=checkbox]', ->
    $(this).parent('form').submit()

При таком подходе даже нет необходимости проверять, что DOM загружен. И также есть дополнительное преимущество: код будет корректно обрабатывать все задачи с чекбоксами, добавленные с помощью AJAX.

Хорошей идеей будет следить за баг-трекером Turbolinks, поскольку на момент публикации статьи у него были серьезные проблемы со сторонними библиотеками, такими как Twitter Bootstrap, Jquery UI Calendar. При этом над ними идет с работа с целью добавления совместимости.

Тем не менее существуют нечастые ситуации, в которых при возврате на предыдущую страницу Turbolinks мило делает POST-запрос, вместо GET. Как решить эту проблему? Просто отключите Turbolinks в подобных ситуациях. Эти детские болезни должны быть вылечены в скором будущем.

Посмотрев на такую кучу проблем можно подумать: «А стоит ли оно того?». Помочь в решении может тест скорости Turbolinks, из которого видно, что в некоторых случаях загрузка страницы повышается в 2 раза. Конечно, все приложения разнятся, но вы можете попробовать гем у себя и проверить, насколько улучшится отзвычивость сайта.

Если же хочется использовать Rails 4 и при этом Turbolinks вам категорически не нравится или не подходит, то его спокойно можно отключить. Для этого необходимо убрать гем из gemfile, а затем удалить соответствующую строку с require из главного JavaScript манифеста приложения.

Спасибо за внимание!

Обо всех найденных ошибках, неточностях перевода и прочих подобных вещах просьба сообщать в личку.



Приложение



Подписывайтесь на мой блог!

Only registered users can participate in poll. Log in, please.

Воспользуетесь ли вы Turbolinks в ваших проектах?

Share post

Comments 29

    +1
    Стоит еще раз подчеркнуть, что применение такого решения не ограничивается RoR.
      0
      django?
        0
        Почему бы и нет — dajaxproject.com
          0
          Turbolinks is not dajaxproject.

          как я понял аналога для djnago нет.
      0
      Если кому интересно посмотреть сайт с турболинками и без:
      old.oratorica.ru/ (Первая загрузка может быть долгой, там выключается сервер, когда в течение 15 минут не заходит никто. Потом будет нормально.)
      и
      oratorica.ru/ (Турболинки и много других оптимизаций)
      • UFO just landed and posted this here
          0
          То есть не парсит снова все стили и яваскрипты.
          0
          Если пользоваться кнопкой back в браузере, все работает «просто замечательно»
            0
            Спасибо за сообщение об ошибке.
            Исправил. Теперь «просто замечательно» :)
          +6
          Если бы проблемы, описанные в этой статье были хотя бы десятой частью… Я недавно по этому поводу писал, просто положу это здесь: borisstaal.com/blog/2013/01/18/dangers-of-turbolinks/. Турболинки – это стильно, модно, молодежно, но будьте аккуратны.
            0
            Спасибо, что поделились ссылкой! Было очень интересно почитать и открывшиеся проблемы меня ужаснули. Очевидно, что гем очень сырой, но есть надежда на то, что его-таки доработают до приемлего состояния.
              0
              В перепалке в твиттере по результату этой статьи, @dhh обещал, что @sstephenson сделает нормальный коллектор мусора при смене страницы. Но, увы, это практически невозможно сделать хорошо. Как я и писал в этой статье, либо он станет еще одним микро-фреймворком, либо под него начнут писать Rails-специфичный код. В любом случае, посмотрим как ситуация будет развиваться. В интересное время живем.
                0
                Одного не могу понять — зачем Turbolinks хранит 10 снимков DOM деревьев, если это все в итоге ломает?

                Писать специфичный код под гем в большинстве своем будут только люди, твердо решившие начально проектировать приложение на Rails 4/Rails 3 с ним с самого начала, как мне видится. С такими проблемами Turbolinks почти невозможно интегрировать в крупные приложения.
                  +1
                  Ломает все не только это, это завершающий гвоздь в крышку гроба. Хранит он их по очень простой причине – это кэш для события «назад». В этом ведь смысл Турболинков – скорость.

                  А что до тех, кто будет его писать, вот вам пример: twitter.com/karthik_ak/status/292935068631044098. Люди просто используют то, что им дают. Хорошо это (coffee, sprockets), плохо это (prototype, например) – не важно. «Rails has beome a trendsetter.» – это ведь действительно так. Только это больше ответственности, чем некоторые думают. И, как по мне, это опасно для комьюнити очень.
            +2
            Турболинкс весьма убог по своей идее. Его единственный плюс в том, что простые приложения (и простые не всегда) работают из коробки.

            Если уж на то пошло, то очень часто хочется обновить только определённую область страницы, например при пагинации или фильтрации. Мы в компании применяем что-то подобное (History API + AJAX) с начала 2012 года, после выхода Turbolinks решили и наше решение завернуть в gem, вот что получилось https://github.com/igor-alexandrov/wiselinks.
              0
              Спасибо, ребята, за этот гем. Очень удобное решение.
                +2
                Не за что! Рады слышать отзывы.
                В ближайшее время напишем пост о разных случаях использования Wiselinks, в том числе о разных «подводных» камнях и способах их решения.
                  0
                  Спасибо! Не забудьте анонс поста на Хабр кинуть )
              +2
              Жалко в голосовании нет, что я использую другую библиотеку реализующую pjax.

              Я считаю, что если используется pjax, то это отличны способ добавить нормальные анимации переключения страниц — вспышки при загрузке современных страниц не очень приятно. Хотя понятно, что анимации нужно прописывать отдельно, а идея turbolinks — дать счастье всем и сразу.
                0
                Я использовал турболинкс в одном проекте, огреб кучу проблем со стороним js, начиная с гугл аналитикса, до гема nested_form.

                Попробовал помочь jquery.turbolinks, github.com/kossnocorp/jquery.turbolinks/issues/8, но в итоге это, похоже, еще больше сломало.
                  0
                  В случаях когда по ссылке нужно перейти обычным способом, можно добавить параметр data-no-turbolink=«true» к контейнеру, тогда все ссылки внутри будут вызывать полную перезагрузку страницы.
                    0
                    А как же SEO? Как поисковики реагируют на данное поведение страниц сайта?
                      0
                      А в чём проблема? Как я понял, для поисковиков каждая страница будет просто загружаться целиком (так же, как и в старых браузерах).
                        0
                        Как пример из хелпа поисковика:
                        Навигация через скрипты. Наиболее распространенным способом размещения ссылки является HTML тег. Но существуют и другие способы навигации между страницами. Например, можно использовать технологии JavaScript или Flash. Робот Яндекса не переходит по таким ссылкам, поэтому следует дублировать ссылки, реализованные при помощи скриптов, обычными текстовыми ссылками.

                        help.yandex.ru/webmaster/?id=1108949

                        Если отключить javascript в браузере и пройтись по страницам — все будет работать?
                          +1
                          В HTML-коде страницы остаются обыкновенные ссылки. JavaScript'ом обрабатываются события click всех ссылок.
                          Отключен JavaScript => нет обработчика => обыкновенные ссылки.
                      +1
                      «Подобная техника похожа на pjax»

                      Тогда уж говорите, что «похоже» означает тождественность:
                      This is similar to pjax, but instead of worrying about what element on the page to replace, and tailoring the server-side response to fit, we replace the entire body. (из описания гема)

                      Нет никакой разницы в том, что использовать, техническая реализация на базе pjax состоит всего лишь в нескольких строках. Также стоит упомянуть о поддержке браузеров. И если уж речь идет об ускорении, то лучше ручками говорить серверу какую часть страницы обновлять. Часто как раз во внешний лейаут входят нагруженные вещи, которые вычислять каждый раз нет смысла — надо обновлять лишь контентную часть страницы.
                        0
                        Как к такой технологии относятся поисковики? Мне кажется велика вероятность получить бан от какого-нибудь Гугла или Яндекса, так как с их точки зрения это очень сильно смахивает на клоакинг — подмену контента для поисковиков.

                      Only users with full accounts can post comments. Log in, please.