kasei_sanのブログ

かせいさんのIT系のおぼえがきです。胡乱の方はnoteとtwitterへ

Turbolinksについて調べてみた

先にまとめ

  • TurbolinksWay(後述)にそって作ればすごい機能
  • 独自実装でリッチなサイトを既に作っている場合、競合地獄がまってるよ!!
  • よくこんなの標準機能にのっけたな

Turbolinksって何?

Rails4.0から採用された、レンダリング高速化のためのgem

fetchReplacementと、fetchHistoryという2つの機能を持つ

  • fetchReplacement : ページ遷移(GETのみ)の代わりにAjaxを使って、<title>と、<body>だけ置き換える仕組み
    • aタグのクリック時の挙動を書き換えている
  • fetchHistory : HTML5のpushStateを使って、ユーザ的には非Turbolinksの場合と差異がないように見せる
    • URLは差し替わる
    • 戻るボタンで戻れる

何が得なの?

  • ページ遷移毎にcssやらjsやらをロード/ブラウザが解釈しなくて良い
  • pjaxっていう似たような仕組みがあった
    • Rails3ではそちらを採用していた様子
    • Turbolinksの方が開発者がやんなきゃいけないことが少ないらしい
      • サーバ側の実装が不要(らしい)

Turbolinks向きでないページがあると聞いたけど?

以下のようなページがある場合は、無効化を検討した方がよいかも

  • 上記にあるように、ページ毎にcss, jsが異なる場合
  • 独自実装で、jsを使って動的に更新しまくるページ
    • 特にイベントハンドリングと、setTimeout/setInterval
    • ページ遷移毎にハンドリングを解除する必要が出てくる

対応ブラウザは?

  • historyAPI に対応しているブラウザ
  • IE10以上、Safari6以上、後最新のChromeFirefox

さっそくだけどTurbolinksを無効にしたい

skip-turbolinks オプションをつけて、rails newする

rails new app_name --skip-turbolinks

一部リンクだけ、Turbolinksを無効にしたい

タグにdata-no-turbolink属性を追加する

<a href="..." data-no-turbolink>No turbolinks here</a>.

$(document).readyが動かないって聞いたけど?

$(document).ready は、Turbolinksでページ遷移する場合は発火しないので、Turbolinksがページ遷移時に発火させるイベントを使う必要がある

Turbolinksが発火させる主なイベント

  • page:before-change : リンクがクリックされた時
  • page:receive : サーバからデータを受信
  • page:change : ページを変更した後(DOMContentLoadedと同タイミング)
    • fetchHistoryの時も発火する
  • page:update : ページを変更した後(jQueryのajaxSuccessと同タイミング
  • page:load : ページを変更した後
    • partial replacement か、キャッシュから復元した場合(fetchHistoryの場合?)は発火しない
    • partial replacement がよくわかってない
  • page:restore : 戻るボタン等で、fetchHistoryが完了した時に発火

で、どれ使えばよいの?

  • page:change を使えば、通常表示時と、遷移時(戻るでも)それぞれに発火するので、基本的にはこれを使う

page:change イベントで、onClickイベント追加したら、ページ遷移した回数だけイベントが発火した!!

  • Turbolinksでは、ページ遷移毎のイベントハンドリングは推奨していない(ように見える)ので、やめましょう
  • イベントは、$(document).ready で全て管理するようにしよう
  • もしくは、ページ遷移前にキチンと解除するようにしよう( 超大変 )
    • その辺がごちゃごちゃしているページの場合、Turbolinks向きで無いので、無効化を検討した方が良い

SEO的にはどうなの?

Turbolinksが変更するのは、<title><body> だけなので、metaタグが更新されないのは大丈夫なの?

  • 大丈夫らしい
  • いいねボタン押した時も、facebook側のクローラーはTurbolinksを経由せず、直接ページを参照するので、metaタグの件も問題ないはず
  • googleクローラーとかはどうなのだろうか

googleアナリティクスどうすんの?

googleアナリティクスに動的ページの場合に、ページ遷移扱いとする処理があるので、 page:change の時にそれを呼ぶ

if (window.ga) {
  ga('set', 'location', location.href.split('#')[0]);
  ga('send', 'pageview');
}

XSS脆弱性があるって聞いたけど

オープンリダイレクタとの組み合わせでできるよ! redirect_to params[:url] とかあって、ユーザがリンクを生成できるサイトの場合

  • ユーザが内部リンク /redirect?url=http://google.com とか作る
  • 生成されたリンクをクリック
  • Turbolinksが、オープンリダイレクタアクションの内容を受けて、 堂々と外部ドメインの内容を表示!!

そもそもオープンリダイレクタとかやめれ

Turbolinks Way

最後に、Turbolinksを使う上でこういうことを意識して設計/実装すると楽だよという個人的な指針

  • 全ページで読み込まれるjs,cssは同一であること(AssetsPipeline前提)
  • Ajaxを使った動的なページ遷移は独自実装せずに、Turbolinksに全て任せる
    • 混ざると大体よろしくないことになる
    • Rails5だとページの一部だけ変更する機能が追加されるらしいので、この部分は更にやりやすくなるかも
  • metaタグは更新されないことを意識する
    • シェアボタンが canonical を見に行くとかやりがち
  • body内に、<script src="*"> を書かない
  • 画面遷移毎にイベントを定義しない
    • $(document).ready で一括にやっちゃうのがベストプラクティスっぽい

ぶっちゃけ相当シンプルなページ向けの機能のような気がする

  • 独自にリッチなページを作りたい場合は無効にしないと、独自実装と Turbolinks が競合しまくって死ぬ未来しか見えない

参考