nisshiee.org

Turbolinks再考

2016-12-04

※ この記事は、はてなブログに私が投稿したものを移管した記事です。

この記事は、「Speee Advent Calendar 2016」の4日目です。
3日目は、@Kosaku_Hidaより、「Google Spread Sheetに記載した複数URLのPage Speed Insightsの点数をGoogle Apps Scriptで取得する方法 (*1)」です。

今日は、Turbolinksを考え直してみます。


みなさん、Turbolinks好きですか?
Rails使ってる人には、結構嫌われてる印象ですが、いかがでしょう?

Railsエンジニアに嫌われるTurbolinksの図

Railsエンジニアに嫌われるTurbolinksの図

なんで嫌われたかというと理由はいたってシンプルで、既存の資産が動かなくなったからです。

という、なかなか悲しい歴史を持ったTurbolinksですが、改めて考え直してみようということで、まずはTurbolinksとは何かを簡単におさらいし、なぜ既存の資産が動かなくなったか、そして、今どきのフロントエンドならどうなのかを考えてみます。

Turbolinksおさらい

Railsの文脈で語られることが多いTurbolinksですが、Turbolinks自体はRailsとは切り離されたJSのライブラリです。

var Turbolinks = require("turbolinks");
Turbolinks.start();

超ざっくりと説明すると、上記のような呼び出しに対して、以下のような処理をします。

  • aタグのclickイベントを乗っ取り、以下のような処理で上書きする
    1. hrefに指定されたURLが自サイト内かどうか判定
    2. 自サイト内であれば、ページ遷移はせずに、XHRでリンク先のページを取得
    3. 取得した遷移先のHTMLを解析し、titleタグやbodyタグなどを抽出
    4. 抽出したタグの中身で、実際に今表示しているDOMを上書きする
    5. HistoryAPIを使って、まるでページ遷移したかのように見せる

キャッシュなどの細かい設定や、ページ遷移エフェクトなどの追加機能もありますが、コアになる機能はこんなとこです。つまり、JavaScriptでクライアント側をいじることにより、まるでSPAのような見た目を実現することができます。

何が問題だったか

jQuery + jQueryエコシステムとの相性が悪かったんです。

jQueryやjQueryプラグイン、jQueryを使った再利用可能なコンポーネントのほとんどは、

  • 特定のクラス名を付けるルールでHTMLマークアップする
  • $(document).readyで特定のクラスが付与されたDOMを探し、セットアップする

という動きを暗黙的にします。もうこれは「jQueryとはそういうものだ」という世界です。ところが、Turbolinksを使うと、ページ遷移がなくなってしまいます。つまり、上記で説明した「4」のフェーズで新しいページのDOMが形成されますが、その後に$(document).readyは発火しないのです。

とは言え、実際にはこの問題を回避する手段も用意はされているのですが、Turbolinks自体の動きをしっかり理解していないと正しいチューニングはできません。また、jQuery以外は自分でコードを書いているようなフロントエンドならともかく、jQueryプラグインやOSSのjQueryコンポーネントを組み合わせてフロントを構築していた場合には、手に負えなくなっても仕方なかったことでしょう。

もうすぐ2017年になろうとしている今

さて、ここまでは過去の話ですが、今はどうか。

なぜ仮想DOMという概念が俺達の魂を震えさせるのか - Qiita

もうこのアドベントカレンダーから2年経つんですね。時が流れるのは早いですね。あれから、いくつかのAltJSが淘汰されて正統ESが復古したり、jQueryを使うのはもはや悪だというぐらいの言われようになったり、ビルドツールも一周回ってpackage.jsonに戻ってきたり、いろんなことがありました。

フロントは流れが早いとか言われますが、とにかく、jQueryを使わずにReactでフロント構築するのが「チャレンジ」だった時代は終わったのです。

では今どきの、Reactの場合はどうなるかというと、Reactコンポーネント側でwindowloadイベントやdocumentDOMContentLoadedイベントをハンドルするようなコードは書きません。つまり、仮にOSSのReactコンポーネントライブラリを組み合わせるようなフロント構築をしたとしても、ライブラリの内部でこれらのイベントに依存していて、Turbolinksとうまく連携しないという心配はまずないわけです。

一方、RailsはずっとデフォルトでjQueryが依存に加えられてきましたが、5.1.0ではついにjQueryが依存から取り除かれるようです。これでRails側もTurbolinksを使う土壌が整っていくでしょう。

Drop jQuery as a dependency #27113 - GitHub

SPAと疑似SPA(*2)

一方で、そもそもSPAを実現したいなら、AngularやReactRouterを使えばいいじゃないかという声もあるかと思います。それはまさにその通りで、Turbolinksが提供するような疑似SPAなんかに頼らずに本物のSPAを実装する体力があるのであれば、それに越したことはありません。その方が、ページ遷移をまたいだデータキャッシュを自由にコントロールできるため、より高いユーザビリティを実現できるからです。

しかしTurbolinksの最大のメリットは、サーバサイドに一切の変更が必要ないという点にあります。極論を言えば、サーバサイドが動的アプリケーションである必要すら無いのです。このサイトは、AWS S3の静的ウェブサイトホスティング+CloudFrontで構築された完全に静的なサイトで、普通のHTMLファイルが置かれているだけですが、Turbolinksで疑似SPAを実現しています。当然、サーバサイドレンダリングについてあれこれ考える必要もありません。

特にスピード重視の新規開発の場面においては、APIとViewを別々に作るのはどうしても煩わしいものです。シンプルなサーバサイドController-Viewを作る速さと疑似SPAによるユーザビリティの両立には一定の価値があるのではないでしょうか。

まとめ

Turbolinks、意外といいぞ。

無条件にrails new --skip-turbolinksせずに、Turbolinksの疑似SPAを味わってみては?


明日は@takanamitoより、「ちょこっとサーバーを立てたいときのTerraformの構成を考える」です。お楽しみに〜

*1
タイトルなげー
*2
「疑似SPA」という呼び方はこの記事上で仮に付けたもので正式名称ではありません