JavaScript
Rails
turbolinks

turbolinksチートシート

More than 1 year has passed since last update.

なんか速くなるやつ、程度の理解だったturbolinksを調べてみました。

「(ロード制御がややこしいので)静的なサイトに関してのみ使うべし」のような意見を多く見かけたのですが、
機能を俯瞰してみると本来はSPAの代替(復数ページでフロントアプリを構成する)を意図したツールのように思えました。
react-redux等と組み合わせての運用を検討してみたいところです。

概要

turbolinks = Ajaxによるページ遷移の高速化のためのライブラリ

  • オープンソース / MITライセンス

  • 導入コストが低い

    • サーバ側の特別な対応は不要
    • モバイル環境にも対応
    • ユーザ側から見て、通常のページ遷移と同じように表示される/動作する
  • Rails4で公式採用された

    • バインディング(turbolinks-rails)がデフォルトでGemfileに含まれている

仕組み

turbolinksにおいて、リンクがクリックからページ表示までの一連の流れをVisitと呼ぶ。
Visitは大きく二種類:

  • Application Visits = リンククリックによるページ遷移
  • Restoration Visits = ブラウザの戻る/進むボタンによるページ遷移

Application Visitsの挙動

turbolinksは以下のように動く:

  • 同じドメイン内にリンクする<a href>タグのクリックを監視する
  • クリック時、リンクによるページ遷移を妨害する
  • キャッシュが存在する場合、その内容を一時的にbody上に表示する(キャッシュプレビュー表示)
  • Ajax(XMLHttpRequest)で次のページを取得する
  • 取得したページを、現在のページと置換える
    • <head>はマージする
    • <body>は完全に差し替える
  • スクロール位置を更新する
  • HistoryAPIを操作して、URLを変える(history.pushState.ページ遷移したように見せる)

遷移にあたって今のページと共有するリソース(css/img/js)の再読込が不要となるため、読み込みが速くなる。
なお、フックイベントを使って遷移をキャンセルできる。

Restoration Visitsの挙動

進む/戻る場合、turbolinksはキャッシュを積極的に利用する:

  • キャッシュが存在する場合は、リクエストを出すことなくページを再表示させる
  • スクロール位置は記録されており、遷移時に復元される

Restoration Visitsはリンクを動的にキャンセルできない

導入

Rails(w/ アセットパイプライン)

Railsかつアセットパイプラインを使う場合は、turbolinks-railsをインストールする(最初からGemfileに含まれている)

Gemfile
gem 'turbolinks', '~> 5'
$ bundle install

それ以外

Rails以外、あるいはRailsでもアセットパイプラインを使わない場合はnpmパッケージとしてインストールする。

$ yarn add turbolinks

使い方

開始する

Rails(アセットパイプライン)の場合は、対象のページが読み込むjsファイルにturbolinksをrequireするだけでいい。

app/assets/javascripts/application.js
//= require turbolinks

npmパッケージとして利用する場合は、turbolinksモジュールをimport/requireした上でstart()を実行して開始する。

app/some/other/system/of/spa.js
// Node
var Turbolinks = require("turbolinks")
Turbolinks.start()

// ES6
import Turbolinks from 'turbolinks'
Turbolinks.start()

利用可否を確認する

supportedプロパティで、ブラウザの対応状況を確認できる。
おおむねHistoryAPIに対応している環境であれば動作するらしい。

if(Turbolinks.supported){
  // ...
}

無効化する

jsの再読み込みなどに関してturbolinksによるリンクに問題が生じた場合は、特定のリンクのみ、この機能を無効化できる。

無効化状態は、対象の<a>タグかその親のタグに、data-turbolinksを指定することで制御できる。

直接無効化する
<a href="..." data-turbolinks="false">turbolinksを使わずにリンクする</a>
親によって無効化する
<div data-turbolinks="false">
  <a href="...">...</a>
</div>

historyスタックを無視する

data-turbolinks-action="replace"を指定すると、ページ遷移しても、その履歴がhistory(戻る/進むボタン)に記録されなくなる。

<a href="..." data-turbolinks-action="replace">...</a>

ロードイベントをとる

turbolinksの場合、ページ遷移をてもwindow以下のオブジェクトの状態が保たれる(メモリ上に残る)。
またwindow.onLoadイベントは、最初のページの表示時以外は呼ばれない

そのため変わりに、画面表示時にはturbolinks:loadイベントが発行される。
このイベントを監視することで、ページ遷移ごとの処理を実装できる。

document.addEventListener("turbolinks:load", function(){
    ...
})

その他のライフサイクルイベントをとる

ロード以外にも、turbolinksの挙動に関わるイベントを取得できる。

イベント 内容 備考
turbolinks:load ページの読み込み時 window.onLoadの代替として利用
turbolinks:click turbolinksが有効な<a>タグのクリック e.data.urlでURLの取得可能
turbolinks:before-visit 遷移の開始前(Restorationを除く) ページ取得のキャンセルも可能
turbolinks:visit 遷移の開始直後 -
turbolinks:request-start ページ取得の開始時
turbolinks:request-end ページ取得の終了時
turbolinks:before-cache キャッシュの開始前
turbolinks:before-render ページ表示前 e.data.newBodyで新しい表示内容を取得可能
turbolinks:render ページ表示後 キャッシュプレビューが表示された場合にも呼ばれる

最初のページ以外では実行させない

<script>タグの実行(評価)のタイミングは、それが書かれた場所によって変わる。

  • <head> 内の場合は、その画面がはじめて表示されたタイミングで1度だけ実行される
  • <body> 内の場合は、その画面が表示されるたびに実行される

繰り返し実行されることによる処理の重複を避けるため、data-turbolinks-eval属性を指定する。
falseを設定することで、その<script>タグはturbolinksを開始したページの初回表示時のみ、実行される。

<script src="path/to/src.js" data-turbolinks-eval="false"></script>

ページ遷移しても特定のタグを残す

data-turbolinks-permanentを指定すると、ページが変わってもそのタグを残すことができる。

<div data-turbolinks-permanent>
  ...
</div>

js/cssファイルの更新を監視する

<head>内に記述されたリソースは初回しか読み込まれない。
data-turbolinks-track="reload"を設定すると、遷移時にリソースの更新を確認し、必要に応じてリロードする。

<head>
  <script src="path/to/src.js" data-turbolinks-track="reload"></script>
</head>

動的に制御する

JavaScriptによって動的にページ遷移させることもできる。

// キャッシュを消す(任意)
Turbolinks.clearCache()

// ページ遷移する
Turbolinks.visit(...)

プログレスバーを表示する

turbolinksによる遷移はAjax制御のため、ブラウザのプログレスバーに進捗が表示されない。
代替として、ロード状況を画面の最上部に表示させることができる。

表示させるためにはまずプログレスバーを有効化する。
(公式の説明ではデフォルト有効のことだが、呼ばないと表示されなかった)

Turbolinks.enableProgressBar()

有効にすると遷移時にclass="turbolinks-progress-bar"のタグが挿入されるので、このクラスのCSSスタイルを任意に設定する。

.turbolinks-progress-bar{
  height: 10px;
  background-color: green;
}

参考