はじめに
Ruby on Railsにはページの遷移を高速化するTurbolinksという機能があります。Turbolinksは優れたJavaScriptライブラリですが、Rails 5からは標準で有効化されているため、Turbolinksを使っているということすら知らずにRailsアプリを作成している人も多いと思います。しかし、Turbolinksの仕様についてきちんと理解していないとバグを生み出す原因になりかねません。
本記事では、Turbolinksの基本的な仕様や、Turbolinksを無効化する方法についてまとめています。
Turbolinksについて
Turbolinksはページ遷移を高速化するJavaScriptライブラリです。Rails 4から正式導入され、Rails 5から標準で有効化されるようになりました。Railsアプリの一機能として語られることが多いですが、元々はJavaScriptライブラリのためRailsアプリでなくとも使うことができます。
TurbolinksはリンクのクリックイベントやWebブラウザのナビゲーションイベント(進む/戻る)を監視し、通常の遷移イベントをキャンセルします。代わりに非同期通信(XMLHttpRequest/Ajax)で遷移先のページを取得し、現在のページのhead
とマージし、body
を差し替えることでページ遷移したように見せかけます。
遷移前/遷移後のページ間でアセット(画像/CSS/JavaScript)が共通化されるため、ページ遷移が高速化されるという仕組みです。
JavaScriptの使用
Turbolinksは独自のライフサイクルイベントやAPIを備えています。これらを適切に使うことで、より便利にTurbolinksを活用することができます。
ライフサイクルイベント
JavaScriptはHTMLが完全に読み込まれる前に実行すると正常に動作しないことが多いので、大抵の処理はロードイベントのブロック内に記述します。
// Pure JavaScript
document.addEventListener('load', function(event) { });
document.onload(function(event) { });
// ES6
document.addEventListener('load', (evnet) => { });
document.onload = (event) => { });
// jQuery
$(document).ready(function(event) { });
$(document).on('load', function(event) { });
しかし、Turbolinksは通常のページ遷移をキャンセルするので、このロードイベントは最初のページが表示されるとき以外は発火されません。Turbolinks使用下では以下のようにturbolinks:load
イベントを使用する必要があります。
// Pure JavaScript
document.addEventListener('turbolinks:load', function(event) { });
// ES6
document.addEventListener('turbolinks:load', (event) => { });
// jQuery
$(document).on('turbolinks:load', function(event) { });
ライフサイクルイベントの一覧は以下の通りです。
イベント | 発生タイミング | 利用可能なデータ |
---|---|---|
turbolinks:click |
Turbolinksの有効なリンクのクリック時に発生。 | url (遷移先URL) |
turbolinks:before-visit |
ページ遷移前に発生。 | url (遷移先URL) |
turbolinks:visit |
ページ遷移後に発生。 | - |
turbolinks:request-start |
遷移先ページを取得する前に発生。 | xhr (XHRオブジェクト) |
turbolinks:request-end |
遷移先ページを取得した後に発生。 | xhr (XHRオブジェクト) |
turbolinks:before-cache |
現在のページをキャッシュする前に発生。 | - |
turbolinks:before-render |
ページをレンダリングする前に発生。 | newBody (新しいbody 要素) |
turbolinks:render |
ページをレンダリングした後に発生。 | - |
turbolinks:load |
最初のページの読み込み後に1回だけ発生。 | timing (遷移タイミング) |
各ライフサイクルイベントで利用可能なデータには以下のようにアクセスします。以下はturbolinks:click
イベントの例ですが、他のイベントも同様の方法でアクセスすることができます。
document.addEventListener('turbolinks:click', function(event) {
console.log(event.originalEvent.data.url);
// => http://www.example.com/destination
});
API
Turbolinks.visit
JavaScriptでTurbolinksによるページ遷移を行いたい場合は以下のように記述します。action
オプションを省略すると"advance"
が使用されます。
const location = '/destination';
Turbolinks.visit(location);
Turbolinks.visit(location, { action: ["advance"|"replace"] });
Turbolinks:clearCache
JavaScriptでTurbolinksのキャッシュを削除したい場合は以下のように記述します。
Turbolinks.clearCache();
Turbolinks.setProgressBarDelay
JavaScriptでTurbolinksのプログレスバーが表示されるまでの時間を変更したい場合は以下のように記述します(単位はミリ秒)。デフォルトは500ms
です。
const delay = 800;
Turbolinks:setProgressBarDelay(delay);
Turbolinks:supported
JavaScriptで現在のブラウザがTurbolinksをサポートしているかを確認したい場合は以下のように記述します。
if (Turbolinks.supported) { }
Turbolinksの無効化
Turbolinksはある場面によっては無効化したい場合があります。無効化するには様々な方法がありますが、Turbolinksの恩恵を最大限に受けるために無効化する箇所は最小限に留めた方がいいでしょう。なるべく小さな単位から無効化していくことを検討してください。
要素単位で無効化
特定のリンクやある要素配下のみTurbolinksを無効化するには以下のように記述します。
index.html.erb
<%# 特定のリンクの無効化 %>
<%= link_to '無効', '/', data: { 'turbolinks': false } %>
<%# 要素配下の無効化 %>
<div data-turbolinks="false">
<%= link_to '無効', '/' %>
<%= link_to '有効', '/', data: { 'turbolinks': true } %>
</div>
index.html.slim
/ 特定のリンクの無効化
= link_to '無効', '/', data: { 'turbolinks': false }
/ 要素配下の無効化
div[data-turbolinks="false"]
= link_to '無効', '/'
= link_to '有効', '/', data: { 'turbolinks': true }
Turbolinksを要素単位で無効化する方法としてdata-no-turbolinks="true"
という属性について書かれていることがありますが、これはTurbolinks 4までの書き方なので注意してください。Turbolinks 5からはdata-turbolinks="false"
を使用します。
ページ単位で無効化
特定のページのみTurbolinksを無効化するには、無効化したいページのhead
に以下のmeta
タグを追記します。
application.html.erb
<meta name="turbolinks-visit-control" content="reload">
application.html.slim
meta[name="turbolinks-visit-control" content="reload"]
アプリ全体で無効化
Railsアプリ全体でTurbolinksを無効化することもできますが、まずは要素単位やページ単位で無効化することを検討してください。
アプリ作成時に無効化
TurbolinksなしでRailsアプリを作成することができます。
$ rails new rails-no-turbolinks --skip-turbolinks
アプリ作成後に無効化
Railsアプリ作成後にTurbolinksを無効化したいという場合は以下の手順を行います。レイアウトapplication.html.erb
またはapplication.html.slim
を以下のように修正します。
application.html.erb
<head>
<%# 以下を修正 %>
<%= stylesheet_link_tag 'application', media: 'all' %>
<%= javascript_include_tag 'application' %>
</head>
application.html.slim
head
/ 以下を修正
= stylesheet_link_tag 'application', media: 'all'
= javascript_include_tag 'application'
Sprocketsを使っている場合は以下の行を削除します。
app/assets/javascripts/application.js
//= require turbolinks
Webpackerを使っている場合は以下の行を削除します。
app/javascript/packs/application.js
require("turbolinks").start()
Gemfile
から以下の行を削除し、bundle update
を実行します。
Gemfile
gem 'turbolinks', '~> 5'
これでRailsアプリからTurbolinksが削除されました。もう一度インストールしたい場合は逆の手順を実行してみてください。
プログレスバーのカスタマイズ
Turbolinksは通常のページ遷移をキャンセルするので、ブラウザにページ遷移の進捗状況が表示されません。そのため、Turbolinksでは独自のプログレスバーをページ上部に表示しています。このプログレスバーはスタイルを設定したdiv
要素なので、スタイルを上書きすることでカスタマイズすることができます。
.turbolinks-progress-bar {
height: 5px;
background-color: green;
}
プログレスバーを表示したくない場合は以下のように記述します。
.turbolinks-progress-bar {
visibility: hidden;
}
まとめ
Turbolinksはインストールしているだけでページ遷移を高速化してくれる便利なJavaScriptライブラリです。しかし、その挙動を理解せずに使い続けていると思わぬ落とし穴に落ちる可能性があります。特に、自分でJavaScriptの処理を実装している場合、誰しも必ず一度はライフサイクルイベントの罠に引っかかるのではないかと思います。
本記事を参考にして、Turbolinksについて理解していただければと思います。