読者です 読者をやめる 読者になる 読者になる

ボクココ

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

Rails5 でフロントエンドを綺麗に扱う方法を考えてみた

Rails

ども、@kimihom です。

SMAP解散騒動が騒がれる中、新規プロジェクトとして Rails 5 のプロジェクトで色々と遊んでみている。その中でフロントエンドの部分の書き方で自分なりに答えを出しながらやっているので、そこの方法をシェアしようと思う。

JavaScript の書き方

まず、最近よくあるフロントエンドのツール(BrowserifyやWebpack, grunt や gulpなど)は使わない想定。あくまで Rails の提供する範囲内で 最大限 JavaScript を美しく保つ方法を記す。

最初はapplication.html.erb のレイアウトから。

<head>
  <title>My App</title>
  <%= csrf_meta_tags %>
  <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css">
  <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track': 'reload' %>

  <!-- CDN -->
  <script src="//....js"></script>
  <!-- app -->
  <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>

</head>

CDN から読み込む系の JavaScript は、jQuery に依存しない JavaScript コードだけを読み込むようにするのが注意。jQuery に依存する場合はapp/assets/javascripts/vendorディレクトリに突っ込む必要がある。jQuery の読み込みは application.js の方でやるため、順番が後になってしまうからである。それさえ気をつければ CDN は1行で読み込みが完了して楽なので積極的に利用していいと思う。

この方法はシンプルが故の欠点があって、JS ライブラリの依存関係とかそういうのを考えないような設計になる。そのため、今後のアップデートで他のライブラリが動かなくなるんじゃないかとかそういう心配をする方にはお勧めできないのかもしれない。ただ、手軽さで言えば断トツでこの方法だろう。

さて、続いて application.js へ移ろう。これは割とシンプルで Rails 搭載の JSライブラリ、外から持ってきたJSライブラリ、自分のJavaScript ファイルの順番で読み込む感じになる。

//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require_directory ./vendor
//= require_directory ./main

続いて個々の JavaScript の書き方。だいたいのイメージがつかめるように、特に意味のないコードを含めている。

let ConsoleCtrl = (function(){
  var localVariable;
  
  function privateFunc() {
    localVariable = "local";
  }

  $(document).on("click", "#console .btn", function() {
    localVariable = "clicked";
  })  

  return {
    publicFunc: function() {
      return localVariable;
    }
  };
}());

いくつか大事な点がある。まず一つ目に(function() {...}()) でくくることで、JavaScript スコープをこのファイル内にとどめるようにしている。結局 Rails は作ったファイルを一つに統合するので、何も考えないでコードを書くと JavaScript のグローバル領域を汚染することになるのだ。function でくくることで、グローバルになるのは、ConsoleCtrl だけになる。そんでlocalVariableを外のJavaScript ファイルから読み込みたい場合は、return で関数を含んだオブジェクトを返すことで対応できる。こうすれば ConsoleCtrl.publicFunc() でメソッドを呼び出すことが可能だ。

そして、 jQuery の書き方は基本的にすべて$(document).on(~~) の書き方になる。これは HTML が常に動的に書き換わるため、ロード完了後のHTMLだけのイベントしか定義できない $("#elem").click(~) の書き方は正しく動作しないためだ。一見動作が遅くなるように見えるけども、何十個もこの書き方をしても、ほとんど速度に関して違和感を感じることはない。

あと最後に、ページを初期化した時に呼びたい JavaScript があった場合にどうするかって話なんだけども、これは jQuery の trigger を使うことで対応している。 Turbolinks にも各種イベントがあるんだけども、全部の JS ファイルに依存するイベントになるので採用しなかった。では実際にどうするのかっていうと、HTMLに triggerを記述する。こんな感じ

<!--  HTML  -->

<script>
  $("#main-ctrl").trigger("main:loaded")
</script>

んで JavaScript 側に通常と同じようにイベントを定義してあげる。

$(document).on("main:loaded", "#main-ctrl", function() {...})

"初期化イベント"っていうくくりで初期化ができるので、割と直感的になっていると思う。

CSSの書き方

これは別に rails 5 に限った話じゃないんだけども、普通に application.css.scss 通りに書くと、変数の読み込みがうまくいかなかったり、Sass をうまく使いこなせていない感がある。てことで、ちゃんと Sass を使う方法として以下のようにしている。

まず application.cssapplication.css.scssに書き換える。そんで以下のようにすべて importでファイルを読み込んでいる。

@import "bulma/bulma.sass";
@import "partials/*";

今回は CSS フレームワークの bulma を使った例。bulma は Sass ファイルのテンプレートを用意してくれてるので、そこに用意されている変数も app/assets/stylesheets/partials ディレクトリ内のすべてのファイルで読み込むことが可能だ。

JavaScript も Stylesheet も、開発時にはキャッシュが効いてしまって色々と面倒なことになりがちなので必ず開発者ツールを起動してキャッシュがかからなくするようにすること。

終わりに

いつまでも Rails のフロントエンドの議論は尽きない。方法は人それぞれだと思うので参考程度にしてみていただけたらと思う。自分のアプリの場合、Gmail のような1ページで収まるシングルページなので、Turbolinks の恩恵はあまり受けないかもしれない。引き続き Rails 5 を触ってみて、Turbolinks 5 や ActionCable などで有用な情報が見つかればシェアしていきたいと思う。