先にまとめ
- TurbolinksWay(後述)にそって作ればすごい機能
- 独自実装でリッチなサイトを既に作っている場合、競合地獄がまってるよ!!
- よくこんなの標準機能にのっけたな
Turbolinksって何?
Rails4.0から採用された、レンダリング高速化のためのgem
- ActionPackの一部
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
- ページ遷移毎にハンドリングを解除する必要が出てくる
対応ブラウザは?
さっそくだけど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 が競合しまくって死ぬ未来しか見えない