Turbolinks※このエントリーで使用している検証環境の各種バージョンは下記の通りです。m※このエントリーの最終更新日は2014.8.11です2013年辺りのRails4について書かれたブログを読むとTurbolinksに関するエントリが結構多いんですね。ググって1ページ目に来るのがこれらのエントリー。やまぶろぐRailsを使ってなかった頃からtwitterとかでちょいちょい流れてくるTurbolinksの記事を流し読み程度に読んでいたのですが、色んな人が解説エントリを書くほど注目されている一方で、何故か無効にする方法がやたら充実しています。使っていいのダメなの??ということが外から判断できないということで、どんな恐ろしい機能なのかとjavaを書きながら横目でチラチラを見守っていたのですが、遂に今の職場で使う機会に巡りあいました。これも何かの運命でしょうか。良い機会ですのでTurbolinksについてじっくり調べてみました。身を持ってその仕様を体験するためにちょっとした実験をやってみたのでその結果をご紹介します。本エントリーはRailsでアプリを作る人がturbolinksを使う時に気をつけること、というレベルでまとめたので、Turbolinksの実装の詳細についてはあまり触れていませんが、冒頭で紹介したリンク先の内容に詳しく記述されていますのでそちらを読むとさらに理解が深まると思います。GitHubでは以下のように説明されています。Turbolinksはあなたのアプリケーションを早くするよ!ブラウザにjs,cssをリコンパイルさせずに現在のページをActiveに保ってbodyとtitleとheadだけを書き変えるよ!pjaxと似てるけど、Turbolinksは何も考えずにbodyをガバっとreplaceしてくれるのでサーバ側で特別なことをせずにpjaxを使うのとほぼ同じような恩恵を得られるよ!もちろん(現在のページをActiveに保つということは)ブラウザ上でjsの長時間のプロセスを持続することになるので、処理の肥大化やメモリリークに気をつける必要があるけど、あなたがファンキーなことをしなければ多分大丈夫だよ!要するにページ遷移(GETのみ)を通常のページ移動ではなくajaxで取得したhtmlでbodyを入れ変えるだけの処理に差し替えることで高速化するgemです。似たようなことをしてくれるライブラリとしてpjaxというjqueryライブラリがあります。Turbolinksにおけるjsの取り扱いしかし手放しで何も考えずに入れとけばちゃんと動くというものでもないようです。最初にロードしたページがActive状態であり続けるjsのプロセスが持続するこの事実を考慮すると今まで書いていたjsがそのまま動くのかちょっと不安になります。console.log('nya-n');});</script>window.onloadの実行タイミングはページ全体がロードされた後のはず。ついでに頻繁に使う$(document).ready迷ったら実験しましょう。各イベントはいつ発火するのかrails4.1.4で簡単なサンプルを作りました。サンプルコードのリポジトリはこちらです。sして実際にアクセスしてみます。リンクをクリックしてindex/indexへページ遷移page:before-changepage:fetchpage:receiveベタ書きしたjsの即時関数ベタ書きした$(document).readypage:changepage:updatepage:loadこの結果から以下のことが言えます。$(window).loadはページの初回表示時のみ実行され、リンクをクリックして遷移した場合は実行されない$(document).readyはページの初回表示時のみ実行され、リンクをクリックして遷移した場合は実行されないテンプレートファイルにベタ書きしたjsはページ遷移毎に実行されるテンプレートファイルにベタ書きした$(documebt).readyはページ遷移毎に実行されるテンプレートファイルにベタ書きした$(window).loadはリンクをクリックして遷移した場合は実行されないassets配下のCoffeScriptに書いた処理はTurbolinksの管理配下の挙動になり、テンプレートファイルに書いたjsはページ遷移毎にそのまま実行されるようです。こちらの記事にも書いてありますが、ページ遷移の度にロードされたhtml内のbodyタグ内のjsタグを取得して、新規のjsタグを組み立て再度差し込んで実行させているようです。処理的には、turbolinks本体のCoffeeScriptを読み込んだ時にAタグをクリックした際にxhrオブジェクトを作ってxhrオブジェクトからリクエストを飛ばす処理をbindしているのですが、ページ遷移時の詳細な挙動はQiitaのこちらの記事に詳しく説明されています。CoffeeScript側のソースだけなら436行しかないのですぐ読めますので気になる方は一度全部読んでみると良いと思います。実装自体は割と素直に書いてありますので読みやすいです。Turbolinks導入のメリット最初に書いたようにTurbolinksを導入することの利点はページロードの高速化です。html全体を再ロードしないため、ブラウザ上での体感速度向上が期待できます。Turbolinks導入のデメリットこれまで書いてきたjsが動かなくなる可能性が高い通常のwebページとjsの挙動が大きく変わるためこれまで作ってきたjsが上手く動作しない可能性が高くなります。これまでRails3で作ってきたrailsアプリケーションをrails4にアップグレードした途端に正しく動かなくなるといったことが簡単に発生します。ページ遷移してもmetaタグが更新されないページ遷移してもCSRF-Token以外のmetaタグが更新されないという挙動になっています。そのため、普通ページ毎に内容が異なるogタグ等もページ遷移しても更新されません。実際にfacebookのいいね!ボタンをクリックしたりした場合においては、フルロードしたコンテンツを各SNSが取得してくれるはずなので問題無いのでは?というようなコメントがついています。yet)」とコメントしています。既存のjsが動かなくなるケース既存のjsをそのまま使おうとした時にどんな問題が起きるか具体例を上げて説明します。$(document).readyにイベントをbindする処理を書いてたら最初にアクセスしたページではbindされるが遷移したらbindされない$(document).readyはリンククリックでのページ遷移では発火しないので、同じjsファイルを読み込んでいるのに「外部リンクから最初にやってきた時しか正しく動かない」ということが起きます。$(document)に同じイベントが多重bindされて正しい挙動にならない全ページで読み込むjsで(例えば共有しているテンプレートファイルにベタ書き)即時関数で以下のbind処理を記述したとします。})();});</script>で、何度もページ遷移してから#bindをクリックするとページ遷移した分だけconsoleに「ニャーン」と表示されます。猫好きの私歓喜!とか言ってる場合じゃありません。これが発生する原因は、最初に書いたとおりjsのプロセスがずっと継続するため、$(document)のオブジェクトはページ遷移してもずっと同一のものが生存し続けています。そのため、ページ遷移する度にbindするようにすると、同一の$(document)オブジェクトに再度bindしてしまうため、何重にも同じイベントがbindされることになります。私も最初この挙動にハマって小一時間悩みました...。1回のアクションでajaxリクエストが複数回飛んでるっぽい?などの分かりにくい問題が起こります。なんでみんなオフにしたがるのか?既存アプリにTurbolinksを導入する場合、「デメリットの方が多くなるケースが多い」ということに尽きると思います。いった所が問題になりそうです。それでもTurbolinksを有効にする場合に気をつけることurbolinksを使用する際のjs実装について気をつけることについて説明します。ページ遷移の度に実行したいjsの実装lightboxやmasonryなどのデザイン系のjQueryプラグインを適用するケースによくある「ページ遷移の度に実行したい」場合。多重実行されるとやっぱり挙動がおかしくなるので、「複数回実行はさせたくないがページ遷移毎に1回だけ実行したい」という要件になります。これを実現するにはいくつか方法があります。テンプレートファイルにベタ書きする毎回実行されることが保証されていますのでテンプレートファイルに記述すれば良さそうです。$(document).readyとpage:loadの両方で実行されるようにassets管理下のjs,coffeescriptに記述する$(document).readyはページ初回表示時にのみ実行され、page:loadはページ遷移時にのみ実行されます。ページ初回表示時には実行されません。同じ処理を両イベントでbindしておけば、常にページ遷移ごとに一回だけ実行されることが保証されます。以下のように記述します。load'これでページ遷移毎に実行され、かつ一回だけ実行されます。ブラウザバックで戻った時にはpage:loadが実行されないので、ブラウザバックで実行されなくて困る処理はpage:changeにbindする必要があります。APIを使っています)、pushStateが使えない場合はリダイレクトされる(location.hrefを書き変えてます)ので大体の場合において問題は起きないと思います。イベントのbind難しいのはこっちで、いくつか方法が考えられますがどの方法を取るにしても開発チーム内で話し合って決めておく必要があります。主な関心事としては「多重bind」をいかに防止するかということになりますが、逆に多重bindされても問題がないようにイベントのハンドラを実装しておくというの手です。前提条件としてテンプレートファイルにベタ書きせず、assetで管理されているjs、CoffeeScriptのファイルに記述することが条件になります。テンプレートファイルにイベントをbindする処理を普通に書くとページ遷移毎にイベントがbindされるので$(document)の場合は上手くワークしません。テンプレートファイルにjsを書かなければいけないケースはControllerから値を渡すようなケースに限定して、使わないように開発チーム内で取り決めをしておく必要があります。1.$(document)にbindする場合ajaxで後からロードするhtmlに対してイベントを定義しておく場合などによく用いられるケースです。assetでapplication.js1つにjsがまとめられる場合は、$(document).ready内でbindするように記述します。e.currentTarget.addClass('hovered')上述の通り初回表示時にのみ実行されるため、初回表示時に$(document)にbindする全てのイベントがbindされその後ページ遷移しても実行されません。これにより確実に一回だけイベントがbindされます。bindするように実装すると上述した通り同じイベントが同じDOMに何重にもbindされて不具合を起こすケースがあります。{callback})の形式に統一してしまうのが良いのではないかと思います。Cordingページごとにjsを読み込む場合は、そもそもturbolinksのページ遷移にならず通常の遷移になるので気にする必要はないですが、turbolinksの恩恵を受けられなくなります。page:load'でbindするようにすればページ遷移毎に実行されますので、これで対応出来ると思います。特定のリンク時のみturbolinksで遷移せず通常のページ遷移をするようにする場合aタグにdata-no-turbolinkという属性を付けます。List</a>全ページでリンククリック時のページ遷移にturbolinksを使わない場合gemファイルとturbolinksを読み込んでる箇所をjs,htmlから削除します。turbolinksのソース読み込んだ時にAタグにbindingされるんでそりゃそうですが。詳しい手順はこちらを参照。まとめ以上、簡単ですがturbolinksの挙動と実際に使う場合の注意点について述べました。文中でも書いたとおり、これまでの色々なweb上でのjsの常識が通じなくなりますので、既存のjsを流用する場合はかなりの手間が必要になります。Angular.jsなどのフロントエンドフレームワークを使う場合においても対応が必要なようです。そのためjsを多用したSPAを作る場合や、開発メンバーが不慣れな場合はturbolinks自体使わないという選択肢もアリだと思います。私も最近初めて使ったのですが最初は色々なものが動いたり動かなかったりでなんじゃこりゃと思いました。Rails4から標準で有効になっている以上、Railsの作法の一つとして実装されているものだと考えています。Wayの恩恵をより受けられるようになっていくのではないかと勝手に考え、今回の機会にまとめてみました。2016の4日目です。3日目は...2016-10-23今読んでる本とか完全に自分用のメモ。(1)検索リンクGitHubseri::diary::graduate_schoolseri::diary::competitive_programmingはてなブログをはじめよう!serihiroさんは、はてなブログを使っています。ブログを報告するスターをつけました引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。