【Rails】Turbolinksの基本情報と実装方法

はじめに

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

JavaScriptでTurbolinksによるページ遷移を行いたい場合は以下のように記述します。actionオプションを省略すると"advance"が使用されます。

const location = '/destination';
Turbolinks.visit(location);
Turbolinks.visit(location, { action: ["advance"|"replace"] });

JavaScriptでTurbolinksのキャッシュを削除したい場合は以下のように記述します。

Turbolinks.clearCache();

JavaScriptでTurbolinksのプログレスバーが表示されるまでの時間を変更したい場合は以下のように記述します(単位はミリ秒)。デフォルトは500msです。

const delay = 800;
Turbolinks:setProgressBarDelay(delay);

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について理解していただければと思います。

関連記事

【Rails】Railsアップグレードまとめ
# はじめに Ruby on Railsに限らず、何らかのフレームワークを使ってWebシステムを構築している場合、フレームワークのアップグレード作業は避けて通れません。 一般的にフレームワークはバージョン毎にEOL (End of Life [...]
2022年10月1日 14:32
【Rails】ユーザー登録時に行うメールアドレス認証機能の実装方法
# はじめに ユーザー登録/解除やログイン/ログアウトといった認証機能の導入に「devise」というGemを使っている人は多いと思います。「devise」では以下のように記述するだけで、ユーザー登録時に確認メールを送付しメールアドレス認証を行う機 [...]
2022年9月24日 14:24
【Rails】モデルに列挙型(enum)を定義し、使いこなす方法
# はじめに Railsはモデルでカラム名と同名の列挙型(enum)を定義することで、カラムと列挙型の変数を紐付けることができます。カラムと列挙型の変数を紐付けると、カラムに対して様々な便利な使い方ができるようになります。 本記事では、モデ [...]
2022年9月3日 10:29
【Rails】RailsでCORSとPreflight requestの設定を行う方法
# はじめに RailsアプリをAPIサーバーとして構築するには、CORS (Cross-Origin Resource Sharing)と Preflight requestの設定を行う必要があります。APIサーバーは外部からの要求に対して処理 [...]
2022年8月27日 10:44
【Ruby】Bundlerを使ってRubyGemsを作成/公開する方法
# はじめに Bundlerを使ってRubyGemsを作成および公開する方法について説明します。Bundlerを使わずにRubyGemsを作成/公開する方法については以下の記事を参照してください。 <iframe class="hatena [...]
2022年7月12日 23:18
【Ruby】RubyGemsを作成/公開する方法
# はじめに RubyGemsを作成および公開する方法について説明します。Bundlerを使ってRubyGemsを作成する方法については以下の記事を参照してください。 <iframe class="hatenablogcard" style [...]
2022年7月11日 21:52
【Rails】M1チップ搭載MacでRuby on Railsの開発環境構築
# はじめに M1チップ搭載MacにRuby on Railsの開発環境を構築する手順を記載します。 - MacBook Air (M1, 2020) - macOS Monterey 12.3.1 # Homebrew ## [...]
2022年5月5日 11:56
【Rails】Rakeタスクの基本情報と作成・実行方法
# はじめに Railsには標準でRakeというGemが同梱されています。RakeはRubyで実装されたMake(UNIX系のOSで使用できるコマンド)のようなビルド作業を自動化するツールです。Ruby Make、略してRakeというわけですね。 [...]
2022年3月7日 22:12