【Rails】アセットパイプラインについてまとめてみた

# はじめに
**※この記事は「RUNTEQ Advent Calendar 2023」の12日目に参加しています**
12月12日のアドベントカレンダーを担当します、さくちゃんと言います🐱
10月からRUNTEQに入学してはや２ヶ月が経ちました。
カリキュラムを進める中でCSS周りで詰まったので、自分の理解のためにもまとめてみました。
同じように詰まって困っている方がいらっしゃれば理解の助けになりますと幸いです。


:::note warn
注意
この記事は初心者が執筆したものであり、誤りが含まれている可能性があります。もし誤りがあれば、コメントなどでご指摘いただけると助かります🙏
:::


# 目次
[1.アセットパイプラインとは](#1-アセットパイプラインとは)
[2.アセットパイプラインの使い方](#2-アセットパイプラインの使い方)
[3.マニフェストファイルを増やしたい場合](#3-マニフェストファイルを増やしたい場合)

# 1. アセットパイプラインとは
Webサイトを閲覧するとき、ユーザーはWebサーバから取得したHTMLを閲覧しますが、それに付随して配信される画像、スタイルシート、JavaScriptといったファイル群をアセットと呼びます。

アセットパイプラインとは、Rails側がアセットを閲覧者に効率的に配信するための仕組みです。

### 主要な機能は大きく分けて４つあります。

## 1. アセットの連結
Webアプリをブラウザで表示するとき、1個の大きなファイルをダウンロードする方がサイズの小さい大量のファイルを個別に取得するよりも効率的です。アセットパイプラインは小さいサイズの大量のアセットを1個の大きなファイルに結合します。

図のように、CSSファイルで4回、JSファイルで４回もサーバーと通信していたら、負荷がかかります。
そこでCSSを１個に、JSを１個にまとめる！というのが「アセットの連結」です。

![Title Page (1).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3551101/bb0b47df-b5b3-debc-08e4-ba232693d69b.jpeg)


## 2. アセットのコンパイル
JavaScript、CSSの記述を快適にするために導入されたSass、Scss 、CoffeeScript などのコードを実際に使用されるcssやJavaScriptへ変換（コンパイル）します。


### 例：CSSとSass

まずは用語の確認。
<details><summary>CSSとは</summary>
HTMLで作られた文書構造にデザインを加えてWebページの見栄えを整える言語。「Cascading Style Sheets」 の頭文字をとったものであり、スタイルシートとも呼ばれる。
</details>
<details><summary>Sassとは</summary>
SassはCSSを簡単に書けるように拡張したものです。Syntactically Awesome StyleSheetsの略。
CSSのすべてのバージョンと完全に互換性があって、任意のCSSライブラリも使うことができる。
CSSの中では、コーディングを一番簡略化できるので人気がある。
SassはRailsでBootstrapを導入するときにも必要。
</details>

HTMLファイルが読み込むことができるのは`.css`ファイルであり、`.scss`ファイルは、`.css`ファイルに変換しなければなりません。

![Title Page (3).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3551101/5321bf90-ea1b-3d3b-8ce1-491decf62aa3.jpeg)

### RailsにSassを導入したい場合

Gemを使用します。
Railsにはデフォルトでsass-railsというgemが導入されていましたが、[Railsガイド](https://railsguides.jp/asset_pipeline.html#dartsass-rails)によると2019年以降メンテナンスされていません。
そのため、Rails7系でSassを使いたい場合は、`sassc-rails gem` の代わりに`dartsass-rails gem`を使います。
`sassc-rails gem`の上位互換が`dartsass-rails gem`みたいなイメージです。

## 3. アセットの最小化
JavaScriptやCSSから不要な空白や改行、コメントを取り除くことでファイルサイズを小さくします。
結果、Webページの高速化に繋がります。

![Title Page (4).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3551101/99696505-cccf-74f0-d072-dec1f5edf446.jpeg)


## 4. フィンガープリントの追加
アセットパイプラインのキャッシュ対策のしくみです。

👉 [そもそもキャッシュとは？](https://wa3.i-3-i.info/word139.html)

フィンガープリントはアセットが変更されると変わるもので、変わった時だけブラウザは新しいアセットを取りに行くようにします。

アセットパイプラインではアセットの内容に応じてファイル名にフィンガープリント(ハッシュ)を追加します。ファイル内容が少しでも変化するとファイル名も変更されるため、ブラウザ側でキャッシュが更新されず表示が乱れるといったことが起きなくなります。

![Title Page (2).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3551101/aca6711a-ae84-7cd4-a58f-46094114cd2a.jpeg)

# 2. アセットパイプラインの使い方

### 1.アセットの作成と編集
app/assetsディレクトリ内にJavaScriptやCSS、画像などのアセットを配置します。


### 2.マニフェストファイルで読み込み設定
`app/assets/application.css`
`app/assets/javascript/application.js`
デフォルトではこの２つのファイルをマニフェストファイルと呼び、どのファイルを読み込むのかを取りまとめたファイルです。
マニフェストファイルがアセットパイプラインの「アセットの連結」の役割を担っています。

```css:app/assets/application.css
/*
 *= require_self　　#このファイル自身を読み込む
 *= require_tree . 　#同階層に存在している全てのファイルを読み込む
 */
```
Sassを使用する場合は`app/assets/application.scss`に`@import`を使用して例えば以下のように記述します。このように使用する言語によってマニフェストファイルの書き方のルールは異なります。

```scss:app/assets/application.scss
@import "top";
@import "post";
@import "bootstrap-sprockets";
@import "bootstrap";
```
JavaScriptのマニフェストファイルでは`//=`で始まる行は、Sprocket(Gem)の独自仕様でアセットパイプラインに指示を与えるためのものです。
```js:app/assets/javascript/application.js
//= require rails-ujs
//= require activestorage
//= require turbolinks
//= require_tree .
```

`//= require`：指定したJSファイルの内容を、記述した位置に取り込みます。（拡張子は不要）

`//= require_tree .`：指定したフォルダ配下にある全JSファイルを結合して、記述した位置に取り込みます。

`//= require_tree .`なので、`app/assets/javascripts`配下にあるJSファイルを全て取り込みます。
`app/assets/javascripts`配下にあるJSファイルを全て取り込みたくない場合は、`//= require`で個別に記載してJSファイルを読み込むようにしてあげます。


### 3.アセットのプリコンパイル（本番環境のみ）

<details>
<summary>config.assets.compileとは</summary>
動的コンパイルをするかどうか制御するオプション。
本番環境ではconfig/environments/production.rb に記載されていることが多い。
開発環では通常、config/environments/development.rbではtrueに設定されていることが一般的。
</details>

<details>
<summary>動的コンパイルとは</summary>
リクエストを受けたときに、最新のアセットがコンパイルされていなかった場合に、その場でコンパイルを実行してアセットを生成する仕組みのこと。
</details>

本番環境では原則`config.assets.compile=false`の状態になっているため、プリコンパイルは手動で行う必要があります。
本番環境では、以下のコマンドでプリコンパイルできます。

```
rails assets:precompile
```

:::note info
開発環境の場合、`assets:precompile`のような明示的なコンパイルを実行しなくてもアセットパイプラインがよしなに動いてくれます。
:::

### +α コマンド紹介
アセットのコンパイルによって生成されたファイルが`public/assets`ディレクトリ内に保存されるため、時折、不要なアセットが残ってしまうことがあります。
そこで、アセットがコンパイルされた結果が保存される`public/assets`ディレクトリ内のファイルを削除します。
```
rails assets:clobber
```
このコマンドを実行することで、古いアセットを削除し、最新のアセットが正しく反映されるようになります。
本番環境でCSSが今まで正常に動いていたのに、`rails assets:precompile`実行後に動かなくなった時などに使用します。

👇 わかりやすい使用例
[【Tips】ローカルで `rails assets:precompile` を実行してしまい以後アセットの更新が反映されなくなってしまった場合の対応](https://qiita.com/GandT/items/4b3701a6ca51ff84370c)




### 4.ビューでアセットを読み込む
アセットはビューファイル（app/viewsディレクトリ内）で利用されます。例えば、`application.html.erb`などで以下のように使用します。

```erb:application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <%= stylesheet_link_tag  'application', media: 'all' %>
    <%= javascript_include_tag 'application', 'data-turbo-track': 'reload' %>
  </head>
```
それぞれに設定されている２つめのオプションの"application"の部分は、
`app/assets/application.css`　
`app/assets/javascript/application.js`
このファイル2つ、すなわちマニフェストファイルを呼び出すことを意味しています。

また、`turbo-rails gem`を使う場合は、`data-turbo-track`オプションも含めることで、アセットが更新されているかどうかをTurboがチェックし、更新されていればアセットをページに読み込むようになります。



# 3.マニフェストファイルを増やしたい場合

管理画面を実装する時など、別のマニフェストを追加したい場合は、`config/initializers/assets.rb`内で`Rails.application.config.assets.precompile`を使用します。
管理者画面のマニフェストを`master`にしたい場合は、コードは次のようになります。
```rb:config/initializers/assets.rb
Rails.application.config.assets.precompile += %w( master.js master.css )
```
一般ユーザー画面では

`app/assets/stylesheets/application.scss`
`app/assets/javascripts/application.js`

の２つを使う。

管理者画面では

`app/assets/stylesheets/master.scss`
`app/assets/javascripts/master.js`

を使うと言うように分けることができます。



### おわりに
記事を進めていく中で新たな疑問点や、深掘りしたい箇所がありましたが、そこまでは到達できませんでした。ふんわりとしか理解できていませんでしたが、それが深まったのは大きな収穫となりました。
引き続き学習を継続して、RUNTEQを卒業する頃には、アプリを作成した記録や痒い所に手が届くような記事を1つでも生み出せたらいいなと思います。
アドベントカレンダーはひさじゅ校長のツイートを見て、完全に勢いでチャレンジしましたが、これを機に記事を書いてアウトプットする習慣を付けていきたい…！貴重な機会をいただきありがとうございました。