テクノロジー

2018年12月28日
  • #アセットパイプライン
  • #Rails

「アセットパイプライン」を学ぼう

Aws4 request&x amz signedheaders=host&x amz signature=2f244d1fb58047efb5fdbe1e085270a99f3c0fd0438bb19d6a5fed616a7fe3b6

Rails初学者がつまずきやすい「アセットパイプライン」

今回は、つまずきやすいポイント「アセットパイプライン」について解説をします。
効果的に学習を進めるためのお役に立つことができましたら、幸いです。

https://diveintocode.gyazo.com/29d9790b75b3045df2ce3e9d816a6e83

アセットパイプライン

Ruby on Railsにはアセットパイプラインという機能が実装されています。その役割をひと言で説明すると、「Web画面上の色塗りを楽にする機能」です。

比喩として”色塗り”と言っていますが、具体的にはWebブラウザに画面のデザインを認識させるためにHTMLに紐付くCSSやJavaScript、Imageファイルを記述・作成することです。

つまずきやすいポイント

初学者が、アセットパイプラインにつまずくのは、「仕組みがよく分からない」ことです。

初学者は、Ruby on Railsのフレームワークの役割とディレクトリ構造を覚えることに苦心します。
そのため、”Webブラウザ上に画面が表示される仕組み”については見落とされがちなのです。

しかし、インターネット上の情報は、Webブラウザを介して見ることがほとんどです。そのWebブラウザ上に画面が表示されるのは、”WebブラウザがHTMLとCSS、JavaScriptを認識する” 必要があることを、理解せずに Ruby on Rails の学習を進める初学者が多いです。

これから以下でWebブラウザがそのように表示されているのかを詳しく説明していきます。

Webの画面は主に、以下3つの言語で構成されています。

* HTML
* CSS(画面のデザインをする)
* JavaScript(画面に動きをつける)
* 画像

Applicationサーバは HTML を動的に生成する役割があります。
逆にCSSやJavaScriptや画像はApplicationサーバから生成されません。

https://diveintocode.gyazo.com/e3205bdcfc3574aa5f9e5358b00f2596

つまりCSSやJavaScriptや画像は動的に生成される処理から無視されたファイルと言えます。
したがってこれらは 静的なファイル と呼ばれているのです。

https://diveintocode.gyazo.com/18b7ca8c243c338436959e23186d407f

さらにもう一つ理由があります。
現在、app/assets/stylesheets内に複数のCSSファイルが存在するのを確認できるでしょうか?

https://diveintocode.gyazo.com/550d9358fa630683975571d20a47a345

Webページを表示するにはこの複数のファイル記述が組み合わさって結合され、一枚の画面上に表示されます。

しかし、Webブラウザには、ひとつのWebページを表示するために複数のファイルを結合する機能は備わっていません。
したがってファイルを 圧縮して結合 するための仕組みが必要となるのです。

そのために アセットパイプライン という仕組みが存在しています。

今までのことをまとめると以下のようになります。

* CSSやJavaScriptや画像は静的ファイルと呼ばれている。
* Webブラウザには、複数のファイルを結合する機能は備わっていない。よって結合するべき機能が必要。

Webブラウザ上で表示されるものは、HTMLとCSS、JavaScriptが紐付くことによって、描かれていると認識しましょう。Ruby on Rails では、この仕組みがアセットパイプラインによって実現されるようになっています。

アセットパイプライン概要

アセットパイプラインは、複数のディレクトリやファイルに分かれたassetsディレクトリ内のファイルをひとつに連結や圧縮する機能です。

(Gemfile)

gem 'sass-rails'
gem 'uglifier'
gem 'coffee-rails'

ここで app/assets ディレクトリ内を覗いてみましょう。
Railsアプリケーションでは以下のような構成になっています。

ディレクトリ名 中身
stylesheets CSSファイル
javascripts JavaScriptファイル
images 画像ファイル

このように、記載する内容の種類ごとにディレクトリやファイルを分けています。
それは開発者にとって、何がどこに記載されているかをわかりやすく認識させるためです。

ここで 一度環境 について復習しましょう。

開発環境は、プログラムを新規開発、修正するための環境です。
本番環境は、修正やテストが完了し正常に動作することが保証されているプログラムを動かす環境です。

その中でアセットパイプラインには環境に応じた以下のルールがあります。

* 開発環境はアセットパイプラインを通る処理が自動実行されるので、CSSやJSや画像が何もせずに反映される。  
* 本番環境ではアセットパイプラインを通る処理が自動実行されないので、手動でアセットパイプラインを実行する必要がある。

アセットパイプラインの仕組み

アセットパイプラインは具体的に以下の手順で行われます。

1. 高級言語のコンパイル
2. 連結
3. 圧縮
4. publicディレクトリに配置

この1~4までの一連の流れを アセットプリコンパイル と呼びます。
アセットコンパイルについては以下で詳しく説明していきます。

gem「sprockets」

アセットパイプラインを実現するためにはgemの導入が必要です。そのgemが sprockets と言われるものです。
sprocketsはGemfileには書いてありませんが、rails をインストールした時に 自動で インストールされるので、 Gemfile.lock には書いてあります。

高級言語のコンパイル

一番言葉の理解に詰まったであろう、「高級言語のコンパイル」に関して説明していきます。

先ほどassets以下に存在するディレクトリやファイルに関しても触れてきました。
そのassetsファイル以下のstylesheetsやjavascriptsのディレクトリに入っているのは CSSやJavaScriptファイル です。
しかし拡張子に着目すると .scss.coffee となっているのに気づきましたか?
これらは 高級言語 と呼ばれています。

高級言語名 拡張子 元の言語
SCSS .scss CSS
CoffeeScript .coffee JavaScript

つまり SCSSCoffeeScript のような高級言語を CSSJavaScript のような元の言語に戻す作業を 高級言語のコンパイル と言います。

連結

先ほどWebブラウザには、ひとつのWebページを表示するために複数のファイルを結合する機能は備わっていないことを述べました。
そこでstylesheetsディレクトリ内のCSSファイルや、javascriptsディレクトリ内のJSファイルを連結する必要があります。
それらの役割を担うファイルが以下2つです。

* app/assets/stylesheets/application.css.scss
* app/assets/javascripts/application.js

(app/assets/stylesheets/application.css.scss)

/*
 *= require_tree .
 *= require_self
 */

(app/assets/javascripts/application.js)

//= require jquery
//= require jquery_ujs
//= require twitter/bootstrap
//= require moment
//= require_tree .

上記はそれぞれのファイルのひと部分です。その中で注目していただきたいのが require_tree . という記述です。
この記述が app/assets/stylesheets ディレクトリ、または app/assets/javascripts ディレクトリのファイルを全て読み込み、連結してくれます。

逆にこの一行が存在しないだけで、ファイルの連結は行われなくなります。

たまに、意味のない記述と勘違いし消去してしまう方がいるので、大変注意が必要です。

圧縮

上記でアセットパイプラインの一つである連結を学びました。今回はアセットパイプラインの圧縮の部分について理解を深めていきます。

CSSやJavaScriptは、可読性をよくするために、スペースや改行を多用しています。

cssファイル

.function {
  font-size: 40px;
  color: red;
}

しかしスペースや改行が増えれば増えるほど、読み込み容量が増えることになってしまいます。
そこでアセットパイプラインはスペースや改行を削除したファイルを作成します。

アセットパイプラインによって構成された読み込みファイル

.function{font-size:40px;color:red;}

これによって書く時は可読性を、読み込むときは読み込み速度を重視したコードにすることができます。

publicディレクトリに配置

今までの工程を経て出来上がったファイルは public/assets に配置されます。

https://diveintocode.gyazo.com/09af4c34a56f488e4bf25a30556fb2bb

CSSファイルやJSファイルの読み込み

ここまで、アセットパイプラインの仕組みや流れ、そしてそれらの連結・圧縮されたファイルがどの場所へ配置されるのかを学んできました。

最後にpublic/assetsに配置されているCSSファイルやJSファイルをブラウザ上で反映させるための仕組みを説明します。
レイアウトファイルを見てみると、以下のような記述があると思います。

(app/views/layouts/application.html.erb)

<%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track' => true %>
<%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>

この2行がHTML変換されると以下のようになります。

<link rel="stylesheet" media="all" href="/assets/blogs.self-d96fd1702932ba528e8e5643dad886ab6efafedbe0b1945a9c0172931dba0da1.css?body=1" data-turbolinks-track="true" />
<link rel="stylesheet" media="all" href="/assets/twitter-bootstrap-static/bootstrap.self-603f330c29af013305325569c8c75d03d688963abd8cde4fffec17b5ce417bfb.css?body=1" data-turbolinks-track="true" />
<link rel="stylesheet" media="all" href="/assets/twitter-bootstrap-static/fontawesome.self-2fe4f8a61d73606fbcb036b41f380591906a4da65a9eb5ec0a210fe6980b2ca7.css?body=1" data-turbolinks-track="true" />
<link rel="stylesheet" media="all" href="/assets/bootstrap_and_overrides.self-285e4f9cadf14699d9ee2852613ea5495ffc4a6888f1bc0a647fd71808607563.css?body=1" data-turbolinks-track="true" />
<link rel="stylesheet" media="all" href="/assets/scaffolds.self-2496bd5cca7ef2e96fd70e6973ab0b6033e7873b98af4edc2b76a3ce7205aa84.css?body=1" data-turbolinks-track="true" />
<link rel="stylesheet" media="all" href="/assets/application.self-dae5c5da7223ce739ad497817a3df9ba2fa2840da302653b11558b4f7d98bfc3.css?body=1" data-turbolinks-track="true" />
<script src="/assets/jquery.self-bd7ddd393353a8d2480a622e80342adf488fb6006d667e8b42e4c0073393abee.js?body=1" data-turbolinks-track="true"></script>
<script src="/assets/jquery_ujs.self-784a997f6726036b1993eb2217c9cb558e1cbb801c6da88105588c56f13b466a.js?body=1" data-turbolinks-track="true"></script>
<script src="/assets/twitter/bootstrap/transition.self-09ff30b1e8a93d1f7728b9855f55d9c9d8d5734c8861e0d8139994e50944572a.js?body=1" data-turbolinks-track="true"></script>
<script src="/assets/twitter/bootstrap/alert.self-a29e91e8cd3ddaba9bbc466901d53ec2127e9256b9b941905d525a3a716bd1a5.js?body=1" data-turbolinks-track="true"></script>
<script src="/assets/twitter/bootstrap/modal.self-72f95ffa1071297725a9ac91989693d56d1abf23f441a47455073b0da2857a5b.js?body=1" data-turbolinks-track="true"></script>
<script src="/assets/twitter/bootstrap/dropdown.self-9314126777c6be5443e37ea7f7967d7914d72b3e60449ba50edc967446373059.js?body=1" data-turbolinks-track="true"></script>
<script src="/assets/twitter/bootstrap/scrollspy.self-a155b9d4b2f978905f0326c0f6635e1134fe91c6bfbfcbad079fa24a9fef2b0e.js?body=1" data-turbolinks-track="true"></script>
<script src="/assets/twitter/bootstrap/tab.self-122235057fbd4c6c7da377d59dc58f47b44cb1088a2e38e6ee6ce9d8ac29a26a.js?body=1" data-turbolinks-track="true"></script>
<script src="/assets/twitter/bootstrap/tooltip.self-11cf547be953f25f511cec668f6690473fd97b2f65502e4032f4030999a3f0c3.js?body=1" data-turbolinks-track="true"></script>
<script src="/assets/twitter/bootstrap/popover.self-77d8e3a2499c1104ef146396a68b82469ee2bdb365199b874694698d10405e9a.js?body=1" data-turbolinks-track="true"></script>
<script src="/assets/twitter/bootstrap/button.self-a6cb16785434acb365ae426aef9f1fce05ed553cae7a965e4471c3da71509175.js?body=1" data-turbolinks-track="true"></script>
<script src="/assets/twitter/bootstrap/collapse.self-7dc8bfbc2fbfabd2bad62c58ff8ffeaf8f20fb87c7ca6cd35f06d4dc19632587.js?body=1" data-turbolinks-track="true"></script>
<script src="/assets/twitter/bootstrap/carousel.self-57eb8422043cf0a85b7a9dc6843916eb0a3e35b419c7798a5eb254b918997631.js?body=1" data-turbolinks-track="true"></script>
<script src="/assets/twitter/bootstrap/affix.self-d2b642d8fbfc1d7041e2edefd66683ada567a980789dcd6f94fddda0b33408b5.js?body=1" data-turbolinks-track="true"></script>
<script src="/assets/twitter/bootstrap.self-fbfa5ad7d9aa0afe439ec4ff3883acc4cb92b62cb67c40d674320c9aa1d4642d.js?body=1" data-turbolinks-track="true"></script>
<script src="/assets/turbolinks.self-c5acd7a204f5f25ce7a1d8a0e4d92e28d34c9e2df2c7371cd7af88e147e4ad82.js?body=1" data-turbolinks-track="true"></script>
<script src="/assets/blogs.self-877aef30ae1b040ab8a3aba4e3e309a11d7f2612f44dde450b5c157aa5f95c05.js?body=1" data-turbolinks-track="true"></script>
<script src="/assets/bootstrap.self-fdc98dee79ee88255e10cac6caa91338165cb76cf0d263744d8d90011fc2ef8f.js?body=1" data-turbolinks-track="true"></script>
<script src="/assets/application.self-f8806224e027f3e3f0138ea9ce99319e298dfdb323304d1f1be6eae8e8c74724.js?body=1" data-turbolinks-track="true"></script>

この二行のHTML変換によって、CSSやJSがブラウザ上で一つのWebページとして反映されます。

アセットプリコンパイルの実行

それではアセットプリコンパイルを実行するにはどうすれば良いか。について以下で説明していきます。
アセットプリコンパイルの実行方法として2つあげられます。

1つ目は以下のコマンドを開発環境上のコンソールで実行します。
こうすることで本番環境上でアセットパイプラインを通るようにプリコンパイル処理を実行してくれます。

  • [x] やってみましょう!

(コンソール)

$ rake assets:precompile RAILS_ENV=production

2つ目は以下の falsetrue に書き換えます。
こうすることで本番環境上でアセットパイプラインを自動で通るようになります。

  • [x] やってみましょう!

(config/environments/production.rb)

config.assets.compile = false #trueに書き換え

以上でアセットパイプラインに関しての説明を終了します。
初心者の方がよくつまづき、仕組みを理解できていないところであるので、今回で理解を深めていただけましたら幸いです。

DIVE INTO CODEのことをもっと知ってみませんか?