Pon De Beach

叩こう ココナッツ アゴゴ

Rails のアセットパイプライン(Asset Pipeline)について

アセットとアセットパイプライン

アセット(Asset)とは、 HTML ファイルに付随するCSS, JavaScript, 画像ファイルといった直接のレスポンスでないファイル を指します。

アセットパイプライン(Asset Pipeline)とは、 複数のアセットファイルをコンパイルし、1つのファイルに統合する ための仕組みのこと。複数あるアセットファイルを1つに統合することでクライアント側のリクエストを減らし、より素早いレスポンスを返すことができるといったメリットがあります。

Sprockets (Rails でアセットパイプライン機能を提供する gem) について

Rails におけるアセットパイプラインという仕組みは、 Sprockets と呼ばれる gem によって実現しています。

Sprockets は以下の機能を提供しています。

  1. アセットファイルにアクセスするためのパスを管理する
  2. アセットのコンパイル
  3. アセットファイル同士の依存性の管理

1つずつ詳細を見ていきます。

アセットファイルにアクセスするためのパスを管理する

様々なディレクトリに置かれているアセットファイルのパスを管理し、 仮想的に1つのディレクトリにまとめて置いてあるようにクライアント側に見せる機能 です。

Sprockets の最も基本的な機能です。

例えば、普段開発を行う上で CSS ファイルは /assets/stylesheets に、 JS ファイルは /assets/javascripts という感じでファイルの種類ごとに分けてアセットパスを管理すると思います。これらの異なるディレクトリ下で管理されているファイル群を /assets に仮想的にまとめることができ、/assets/main.css というようにアセットファイルを参照することができるようになります。

Rails におけるアセットファイルの置き場所

rails new をした時点であらかじめいくつかのアセットパスが用意されています。

  • app/assets …アプリケーションの主要な機能に関わるアセットファイル
  • lib/assets複数のアプリケーションで使われるような機能に関わるアセットファイル
  • vendor/assets …外部から取得したアセットファイル

CoC (設定より規約)に反しないためにも、適切な場所に適切なアセットファイルを置くように心がけましょう。

config/application.rb でアセットパスの追加ができます。

アセットのコンパイル

JS や CSS のアセットファイルを コンパイル してくれます。元々 JS, CSS のファイルは何も起きません。

Rails ではデフォルトで CoffeeScript, Sass をコンパイルします。

例えば、 hello.js.coffee といった拡張子が連なって記述されたようなファイルを Rails でみかけたことがあるとおもいます。 Sprockets では拡張子からファイルの種類を判別しコンパイルします。この場合は、 hello.js.coffee.coffee から CoffeeScript ファイルと判別しコンパイルして、最終的に生 JS ファイルとして出力されます。

この拡張子複数つけることも可能 です。

hello.js.coffee.erb というアセットファイルは、 ERB → CoffeeScript という順番でコンパイルを行い JS ファイルとして出力されます(後ろの方から順番にコンパイルが走る)。

コンパイルする種類は増やすことができます。

TypeScript を追加したい場合は、以下の方法で可能なようです。

Ruby on RailsでTypeScriptを使ってみよう! | nanapi TechBlog

アセットファイル同士の依存性の管理

アセットファイル同士で起こる依存関係を『マニフェストファイル』と呼ばれるファイルで管理することができます。依存関係というのは、例えば hoge.jsjQuery を使ってる…みたいなパターンです。hoge.jsjQuery の機能を使っているので、 jQuery がないと動かない、つまり依存関係にあるということです。

rails new した時に作られるマニフェストファイルは app/assets/javascripts/application.jsapp/assets/stylesheets/application.css になります。

実際に中身を見てもらうとわかりますが、 //=require jquery という記述があります。この記述がアセットファイル同士の依存性を管理している構文になります。コメント内で記述されているのはその他のツールで読み込んだ時に影響を与えないようにです。

require の部分は ディレクティブ と呼ばれ、 require 以外にも何種類かディレクティブがあります。その一部を紹介します。

ディレクティブ 説明
require 引数として与えられたファイルが自身よりも前に挿入される。2回目以降呼び出されても読み込まれない
include require と同じ動きをするが、2回目以降呼び出されると都度ファイルの内容が挿入される
require_directory 与えられたディレクトリ以下のファイルを、自身よりも前に挿入する。順番はアルファベット順(さらに大文字→小文字)になる
require_tree require=directory と同じ動きをするが、再帰的に読み込む

読み込みはコードの 上から順番に 読み込まれます。

Rails で生成したマニフェストファイルには //=require_tree . という記述があります。//=require_tree . はカレントディレクトリ以下の全てのファイルを再帰的に読み込んでいきます。自作したアセットファイルが自動で読み込まれるのは //=require_tree .マニフェストファイル内にデフォルトで書いてあるからですね。

開発環境において、マニフェストファイルが正常に読み込まれているかデバッグするためのヘルパーメソッドがあります(stylesheet_link_tag, javascript_link_tag)。これを利用することでマニフェストファイルで読み込まれた順にアセットファイルが記述されていることを確認できます。

また、 config/environments/development.rb 内で config.assets.debug=true と設定しなければなりません(デフォルトは true)。false の場合は本番環境と同じく1つのアセットファイルに纏められます。

アセットファイルのプリコンパイル

リクエストが来た時点で事前にコンパイルしたアセットファイルを渡すことができる機能です。

$ rake assets:precompile

というコマンドでアセットファイルのプリコンパイルを行なうことが出来ます*1

プリコンパイルを行なうことで、リクエストのたびにコンパイルをする手間が省けより効率的にレスポンスを返すことができるようになります。

Rails のデフォルトでプリコンパイルはしてくれますが、デプロイ時にプリコンパイルした静的ファイルを一緒にデプロイするのを忘れずに。

フィンガープリントについて

Sprockets でコンパイルされたアセットファイルは ファイル名-MD5 値.rb という形式のファイル名になります(例えば application.css のフィンガープリントは application-5eece9848f909164a45045f6a24bc6b1.css となる)。 MD5 値は元のファイルの内容から生成されたもので、これはファイルの更新があったかファイル名を参照するだけで済むというメリットがあります。

参考

書籍:パーフェクトRuby on Rails(p.94~104)

アセットパイプライン | Rails ガイド

*1:Rails 5 からは rake じゃなくて rails に統合されたんだっけ