ボクココ

サービス開発を成功させるまでの歩み

Rails x jQuery でコードをカオス化させないコツ

ども、@kimihom です。

f:id:cevid_cpp:20190211160008j:plain

今でも jQuery で動いている Web アプリケーションは多いことかと思う。jQuery は特性上、ソースコードがカオスになりやすいので、複数人で Web アプリケーションを開発するとすぐにメンテしたくない程のシステムができあがってしまう。そこで別の JavaScript フレームワークへ一気に移行するってのが最近のトレンドではあるが、もっとシンプルに jQuery のリファクタリングによって改善する上での Tips をお届けする。

コントローラー毎に厳密に分ける

Rails でコントローラを作成する際のお決まりコマンド rails generate controller によって、JavaScript や CSS もコントローラ毎に生成される。このルールをしっかりと守ろう。

View

<div id="user-controller">
  
</div>

CSS (SCSS)

#user-controller {

}

JavaScript

$(function() {
  $(document).on("click", "#user-controller .submit", function() {
  });
});

こうすることで、意味不明なバグやぐちゃぐちゃなスタイルになるリスクが一気に減る。まずは基本を抑えておこう。

もしビューで共通化したい項目が出てきた時は、 <%= render "~" %> を使って部分テンプレートとして書き出そう。JavaScript で処理を共通化させたい場合には、別の JavaScript ファイルを生成しておけば解決する。共通のコードを呼ぶけど、メインとなるコントローラがある(例えば Signout を他の JavaScript から呼びたい)場合には、後述する Trigger を使う方法もある。

Turbolinks と rails-ujs を愛す

Turbolinks は Rails で生成したリンクを "Ajax + History API"化するものである。

Turbolinks を使うことで、ブラウザの「戻る・進む」を Ajax 通信をしたとしても動作させることができるようになる。これは裏側で HTML5 の History API を使っているのだが、Turbolinks はそれをうまくラッピングしてくれている。もちろん、自前で History API を実装することも可能だが、Turbolinks を使えば 「戻る・進む」の同じページをキャッシュして表示してくれたり、どうせ自前で必要になるメソッドがすでに用意されていたりする。

rails-ujs は HTML のフォーム処理を ActionView とつなぎ合わせるためのものである。この使い方に関しては以下の記事に詳しく説明してある。

参考: www.bokukoko.info

この2つを駆使すれば、シングルページアプリケーションを Rails Way に乗っとった形で実現できる。Turbolinks/rails-ujs/ActionView を使いこないしてもないのに、Rails だけでシングルページアプリケーションはできないから別のフロントエンドフレームワークを入れるみたいな判断は時期尚早だと言える。Rails の form_with を用いることでフロントエンドの処理をサーバー・DB側の定義と一致させて一気にコーディングの手間が減るメリットもある。

基本的にフォーム送信等の Ajax 送信を生の jQuery $.ajax を使わないのを意識しよう。全て rails-ujs で定義されている ajax:success などのイベントで、送信した後の処理だけ書くようにしよう。これだけで JavaScript 側で Ajax送信コードがなくなる分、シンプルになる。

$(document).on("ajax:success", "#user-create-form", function(e) {
  console.log(e.detail[0]);
});

レスポンスで JSON を返さない

Ajax で呼び出すときに、JSON を返してViewの構築はフロントエンド側でやりたいことが出てくるだろう。しかし、これをやっちゃうと Rails 側の View ヘルパを活用できないので、Rails 側でせっかく提供されている ActionView が使えずに全部自前でやらないといけないケースが出てくる。それを阻止するには、Ajax で何かリクエストを呼んだとしてもレスポンスは html.erb にすることだ。成功か失敗かの判断くらいでいい場合(ユーザー作成できた、削除できたetc) の時などで JSON を返すようにしよう。 これで jQuery の DOM 操作を減らすことができる。

$(function() {
  $(document).on("ajax:success", "#hoge-link", function(e) {
    // HTML レスポンス
    var res = e.detail[2].response;
    $("#main-wrapper").html(res); 

    // JSON レスポンス
    var res = e.detail[0];
    if (res.result == "ok") alert("created"); 
  });
});

Trigger を駆使して View に書く JavaScript を減らす

jQuery の trigger は意外と知られていない便利機能だ。trigger を呼ぶだけで、特定のイベントを任意のタイミングで呼び出すことができる。これを活用することで、View 側に書く必要のある JavaScript を一気に減らすことができる。 例えば、Turbolinks で遷移した最初にセットアップで JavaScript を呼びたいってケースなどで特に有用だ。他にも例えばuser.js にあるコードを article.js から呼び出したいってときにもトリガを使えばグローバル関数を使わずにシンプルに表現できる。

<div id="user-controller">

</div>

<script>
$("#user-controller").trigger("loaded");
</script>
$(function() {
  $(document).on("loaded", "#user-controller", function() {
    // initialize
  });
});

参考: bit.ly

終わりに

本記事では Rails x jQuery でカオス化させない方法についてご紹介した。「参考」で紹介した2つの記事はボクココの中でもかなり読まれている記事なので、既にこれらを実践している方も多い印象がある。

Rails x jQuery の組み合わせ方の方が、歴史が長くメンテナンスされていることは間違いない。もちろん、jQuery 等を使わないことで差別化したい何かがあれば他のを使う選択も間違ってはない。ただフロントエンドに特にこだわりがなくシンプルな Web アプリケーションを作るってなら依然として間違ってない選択肢であろう。

現状の Rails x jQuery のコードがカオスだった場合、上記のようなリファクタリングによって改善ができないか検討してみよう。