もともとRuby on Railsの一部だったturbolinksですが、Rails5からはturbolinksは独立し、Rails以外のプロジェクトでも使えるようになりました。そこで、静的サイトでも使ってみることにしました。かなり体感速度が上がったので、オススメです。
turbolinksとは?
turbolinksとは、ページの移動をすることなく、body内のデータの入れ替えと、head内のデータのマージを行うことで、表示を高速化するライブラリです。
ページの移動をしないため、新たにJSファイルやCSSファイルのダウンロードやキャッシュの確認もすることがなく、かなり速く感じます。
turbolinksのメリット
- 表示が速い
 - ページがキャッシュされるので一度表示したページは高速に表示される
 
turbolinksのデメリット
- jQueryのライブラリで動かないものがある可能性がある(
$(document).readyでのみ評価するものとか) - 戻るボタンで戻ったら動かなくなるものが(略
 - 
window,documentオブジェクトは変更されないので、そこを意識する必要がある 
とはいえ、対処できないものはあんまりないと思います。
インストール
npm
turbolinksはnpmで提供されているので、導入も簡単です。
$ npm install turbolinks --save
es2015とかで使う場合はrequireしておく必要があります。
const Turbolinks = require("turbolinks");
Turbolinks.start();
jQueryユーザーのためのturbolinks入門
イベントリスナー
$(document).readyイベント
turbolinksを使うと、画面は1度しかロードされないので、$(document).readyが一度しか実行されません。
そのため、ページを移動するとそれまで存在しなかったDOMに対してのイベント類が設定されません。
turbolinksは独自にイベントリスナーがあるので、それを使います。
$(document).readyの代わりに、document.addEventListener('turbolinks:load', function(event){});を使います。
$(document).ready(function() {
    $(".foo").on('click', function(event) {
        console.log("clicked!");
    });
});
document.addEventListener('turbolinks:load', function(event) {
    $(".foo").on('click', function(e) {
        console.log("clicked!");
    });
});
$(window).loadイベント
windowオブジェクトも変更されないので、このイベントも1度しか実行されません。
よって、なるべく使わないように変更したほうがいいでしょう。
turbolinksのイベント一覧
loadだけでなく、turbolinksには多くのイベントがあります。
それをうまく使えば、デメリットに書いていた部分はほぼほぼ解消できると思います。
| イベント名 | 説明 | 
|---|---|
| turbolinks:click | リンクをクリックしたときのタイミングで実行される | 
| turbolinks:before-visit | リンク先に移動する前に実行される | 
| turbolinks:visit | リンク先に移動しようとしたらすぐ実行される | 
| turbolinks:request-start | ページを取得するためのネットワーク通信を始めるときに実行される。ヘッダにデータを追加する必要があるときなどはここで処理する。戻るボタンだと発火しない(キャッシュしている場合) | 
| turbolinks:request-end | ネットワーク通信が終了したら実行される。通信エラーなどはここで処理する。戻るボタンだと発火しない(キャッシュしている場合) | 
| turbolinks:before-cache | ページがキャッシュされる前に実行される。キャッシュされるとDOMは残るけれどイベントが解除されてしまうので、不要なDOMの削除とかはここでしておく | 
| turbolinks:before-render | ページがレンダリングされる前に実行される | 
| turbolinks:render | ページがレンダリングされたら実行される。2度呼ばれることがある。キャッシュ表示で1度目。実際にデータを取得して2度目 | 
| turbolinks:load | ページが表示されたタイミングで1度目が実行される。あとは移動するたびに実行される。戻るボタンで遷移しても実行される | 
静的なサイトの場合は
turbolinks:loadturbolinks:before-cache
くらいしか使いませんでした。
JS, CSSの置き場はheadタグ内に
CSSはともかくとして、JSファイルの読み込みはbodyタグを閉じる直前に書くのが一般的になってます。
これは、htmlのレンダリングをJSのダウンロードよりも先に行わせるためのテクニックですが、turbolinksを使う際にはこれではいけません。
<body>
  ...
  <script src="/assets/application.js"></script>
</body>
turbolinksを使う場合、bodyタグ内にJSファイルを置くと、毎回ダウンロード・実行されてしまい、高速化どころか遅くなりますし、挙動がおかしくなります。
head内に移動させましょう。
<head>
  <script src="/assets/application.js"></script>
</head>
<body>
  ...
</body>
windowオブジェクトへのイベント定義について
turbolinksを使うと、window, documentオブジェクトは変更されません。
そのため、これらのオブジェクトに対してturbolinks:loadのタイミングでイベント設定をしていたら、ページを移動するたびにイベントを設定してしまうことになります。
// 基本的に1度しか実行されなかったのでこれでもよかった
$(document).ready(function(){
    $(window).scroll(function(e) {
        // スクロールしたら何かやる
        console.log("scroll");
    });
});
// ページ遷移する度にwindowに対してイベントが設定されるので絶対にダメ
document.addEventListener('turbolinks:load', function(event) {
    $(window).scroll(function(e) {
        // スクロールしたら何かやる
        console.log("scroll");
    });
});
これはそもそも、$(document).readyのタイミングで定義する必要のないものなので、イベントリスナーの中で定義するのをやめましょう。
$(window).scroll(function(e) {
    // スクロールしたら何かやる
    console.log("scroll");
});
document.addEventListener('turbolinks:load', function(event) {
    // 他の処理
});
$(document).on も同様
実は$(document).onの定義も、$(document).readyの中でする必要はありませんでした。なので、これらも外に出します。
// 動的に追加する要素に対してのイベント設定はturbolinks:loadで設定しない
$(document).on('click', '.item', function(event) {
    console.log("item clicked!")
});
document.addEventListener('turbolinks:load', function(event) {
    $(".foo").on('click', function(event) {
        // turbolinks:loadで設定するのは静的な要素に対してのみ
        console.log("foo clicked!");
    });
});
まとめ
静的なサイトに関していえば、これくらいで十分でした。
また何かTipsがあったら追加していきます。