【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】Paranoiaを使用した論理削除(ソフトデリート)
# はじめに Paranoiaは、Railsアプリケーションで論理削除(ソフトデリート)を実現するためのGemです。 論理削除は、データベースのレコードを物理的に削除するのではなく、削除フラグを設定することで「削除済み」とみなす方法です。こ [...]
2024年7月20日 21:33
【Rails】activerecord-multi-tenantを使用したマルチテナントアプリケーションの作成
# はじめに マルチテナントアプリケーションでは、複数の顧客(テナント)が同じアプリケーションを利用するため、データの分離が必要です。 activerecord-multi-tenantは、このようなマルチテナント環境をサポートするための便 [...]
2024年7月18日 16:50
【Rails】RubyとRailsにおけるattr_reader, attr_writer, attr_accessorの概念と使用方法
# はじめに RubyとRailsの開発において、`attr_reader`,`attr_writer`,`attr_accessor`は非常に便利なメソッドです。これらは、クラス内でインスタンス変数に対するゲッターおよびセッターメソッドを簡単に [...]
2024年7月17日 18:11
【Rails】RubyとRailsにおけるyieldの概念と使用方法
# はじめに RubyとRailsにおける`yield`は、メソッドやテンプレートの中で動的にコードブロックを実行する能力を提供し、これによってコードの再利用性と拡張性が大幅に向上します。本記事では、RubyとRailsにおける`yield`の概 [...]
2024年7月17日 13:15
【Rails】AASMを使用してオブジェクトの状態遷移を効率的に管理
# はじめに Railsアプリケーションにおいて、オブジェクトの状態管理は重要な課題の一つです。AASM (Acts As State Machine) gemは、複雑な状態遷移を効率的に管理します。本記事では、AASMの基本的な使い方を解説して [...]
2024年7月16日 18:00
【Rails】RSpec + Swagger + rswagでアプリケーションのAPIをテストおよびドキュメント化する方法
# はじめに Railsアプリケーションの開発において、APIのテストとドキュメント化は重要な要素です。 RSpecはテストフレームワークとして広く利用されており、SwaggerはAPIの設計とドキュメント化を支援します。これらを統合するr [...]
2024年7月16日 14:27
【Rails】mailcatcherを使用して開発環境でメール送信をテストする方法
# はじめに mailcatcherは、開発環境でのメール送信をキャプチャするためのツールです。ローカルで送信されたメールをブラウザ上で簡単に確認できるようにします。mailcatcherをRailsアプリケーションで使用する方法について説明しま [...]
2024年7月15日 16:37
【Rails】impressionistを使用してページビューやクリック数を追跡する方法
# はじめに impressionist Gemを使用してRailsアプリケーションでページビューやクリック数を追跡する方法について説明します。 # 実装方法 ## impressionist Gemのインストール まず、impre [...]
2024年7月15日 14:18