Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

Railsのフロントエンドのノウハウ#2: JavaScript編(翻訳)

概要

原著者の許諾を得て翻訳・公開いたします。

記事を2本に分割しました。日本語タイトルは内容に即したものにしています。

Railsのフロントエンドのノウハウ#2: JavaScript編(翻訳)

RailsにおけるJavaScriptについての洞察

Rails開発者の多くは、JavaScriptがうまく動作しない場合の振る舞いについてなかなか把握できないことがあります。しかもRailsの場合、JavaScriptのいくつかの振る舞いについても特定の方法で変更されます。

TurbolinksやSpringを嫌う人が多いという話を聞いたことがあるかと思います。私はたまたまどちらの技術も好きですが、これらの動作を理解しておかないと、百戦錬磨のJavaScript開発者ですら頭を抱えるような事態になるかもしれません。

これら2つのライブラリについて簡単にご説明いたします。

Turbolinksは、ページの読み込みを高速化したり、フォームや一部のCRUD操作で使えるUJS機能を素早く読み込めるようにしたりします。Turbolinksを導入するとJavaScriptの「page ready hook」が変更されます。このフックは"turbolinks:load"を行うのに使われます。Turbolinksを使っていると、標準のJavaScript「page ready hook」でいくつかの問題が生じます。

Springはアプリのコードを対象とするプリローダであり、Rubyコードとフロントエンドのアセットの両方について読み込んだものを自身のプロセスにキャッシュし、次回のリクエストで使えるようにします。JavaScriptのエラーハンドリングについての知識がないと、解決に多大な日数を要するバグを追いかけるはめになるかもしれません。Springは、最初のページ読み込みから最初に操作を行った「後で」これらを機能させるためです。したがって、サイトが読み込まれるときに一部が動かないことがあっても、リンクをクリックすると動き出します。

Springの役割を詳しく調べる前に、JavaScriptがひどいコードをどのように扱うかを最初に見てみることにしましょう。

Javascriptが何らかのひどいコードに遭遇したり、評価すべきでないものを評価したりすると、JavaScriptは現在の作業を停止して、何か問題が生じていることをブラウザのコンソールに出力します。すなわち、アプリにJavaScriptの何か新しいライブラリを導入し、そこにひどいコードがあると、それ以降のコードはライブラリであろうと自分のコードであろうと動かなくなります。これはJavaScriptの通常の振る舞いであり、ひどいコードを実行する時点でコードの事項を停止するためのものです。

しかしSpringを追加した場合、アセットがキャッシュに(Turbolinksもろとも)読み込まれてひどいJavaScriptコードが評価された後、SpringはJavaScriptコードをの実行を回避しません。つまり最初のページを読み込むと、SpringやTurbolinksがアセットやライブラリをキャッシュしてより高速読み込みに備えるのです。

最初のリンクを操作してたどるまではまったく問題ないように見えることがあります。というのも、SpringはひどいJavaScriptコードの後でよいJavaScriptコードを生成するためです。最初のページ読み込みでJavaScriptの機能が不要であれば、何か問題が起きているかどうかをコンソールで確認しない限り、何も問題が起きていないように見えます。そして(productionで)コードを公開すると、「ローカルではちゃんと動く」にもかかわらず、サイトの一部がまったく突然にダウンして期待どおりに動かなくなります。

この手の「ローカルと同じ結果をproduction環境で得られない」問題のせいで、多くの人が髪の毛をかきむしりまくっています。

ここでJavaScript開発者にひとつ耳寄りな小技をご紹介します。アプリに新しいライブラリなり新しいJavaScriptコードなりを追加する場合は、常にその末尾にconsole.log("Seems okay ¯\_(ツ)_/¯");を追加しておくというものです。こうしておけばブラウザコンソールでコードがちゃんと動いていることを確認できるようになります。コンソールログにこれが出力されていれば、自分のJavaScriptコードが読み込み中に「ひどいコード」として評価されていないことがわかります。

私はBootstrap 4ライブラリをいろんなバージョンで試しましたが、その中に「ひどい」と評価されるJavaScriptコードも含まれていたことがありました。しかも私はそれを、アプリで必要とされる他のJavaScriptライブラリより前に配置していたのです。おかげで最初のページ読み込みでJavaScript関連のコードが軒並み動かず、ページをリフレッシュしたり何らかのリンク操作を行ったりした後でどうやら動くという事態になりました。私がBootstrap 4ライブラリをTurbolinksより前に置いていたために、コードの振る舞いがそのようになってしまったのです。Springがあっても最初のページ読み込みで問題なく動くように、CoffeeScriptでささやかなハックを仕込みました。

if !Turbolinks?
  location.reload

Turbolinks.dispatch("turbolinks:load")

これはローカル環境では問題なく動作したのですが、productionでは動きませんでした。Rails開発者は、"turbolinks:load"トリガがWebサイトの最初のページ読み込みで当初呼び出されないという問題をちょくちょく踏んでいます。

Turbolinks.dispatch("turbolinks:load")は、JavaScriptのその部分のコードが評価された後できるだけ早い段階でこのWebフックを即座にトリガするための方法です。これは、最初のページ読み込みの時点でJavaScriptやCoffeeScriptのスクリプトをトリガしたい方にとって役に立ちます。

ページ読み込み時にCoffeeScriptをトリガする場合、ちょっと一手間かける必要があります。というのも、CoffeeScriptコードはすべて1つの関数内にラップされていて、スコープが保護されているためです。Turbolinks.dispatch("turoblinks:load")メソッドを使うことで、このあたりのややこしさが少しマシになります。

Springを使っている場合に有用なハックはif !Turbolinks?; location.reloadしかありません。これが必要になるのは、JavaScriptの何らかの「ひどいコード」が評価されるdevelopment環境でJavaScriptコードを読み込む場合だけです。これを使うのではなく、先ほどのconsole.logテクニックを用いてJavaScriptコードの実行に成功したかどうかを確認しましょう。その後で、「ひどいコード」として評価される状況を改善しましょう。

最後に

Capybaraは、フロントエンドのエクスペリエンス全体のテストをシンプルに書ける素晴らしいライブラリです。RailsはCapybaraテストの大半を代わりに生成してくれるので、それを読んで学べば同じようなテストを今後も書き続けられます。面倒な設定はRailsが肩代わりしてくれるので、フロントエンドのテストを始めるのがとても簡単になります。

Rails 5のもうひとつうれしい点は、システムテストで例外が発生すると、ブラウザウィンドウの内容のスクリーンショットをtmp/screenshots/に自動保存してくれるようになったことです。画面とスクリーンショットを比較してテストの問題を特定しやすくなりました。

Railsのライブラリと併用した場合のJavaScriptの振る舞いを理解しておけば、地雷を避けてデバッグ時間を大幅に節約できるようになります。JavaScriptの振る舞いを正しく理解すれば、これまでうんざりさせられていた強力なツールたちをさらに使いこなせるようになります。TurbolinksSpringは、効用をと利用法を理解しておけば素晴らしいツールになるのです。

おかげで近頃はRails開発者でよかったと思えます。フロントエンドテストで大いに楽しみましょう!

Twitter: x"Rails Frontend Testing with JavaScript Insights" via @6ftdan

ツイートより

関連記事

Rails: システムテストをRSpecで実行する(翻訳)

Rails 5: WebSocketのマルチセッションをMiniTestとシステムテストでテストする(翻訳)


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。