Railsにアセットパイプラインが２つ存在する理由　sprockets webpacker について

##はじめに
Railsのアセットパイプラインについて、webpacker sprocketsの２つのアセットパッケージングツールがデフォルトで共存していることを疑問に感じたので、その違いについて詳しく調べてみました。
##アセットパイプラインって？
>アセットパイプラインとは、JavaScriptやCSSのアセットを最小化 (minify: スペースや改行を詰めるなど) または圧縮して連結するためのフレームワークです。

引用：[アセットパイプラインについて](https://railsguides.jp/asset_pipeline.html)
rails５.１からは、以下の２つのアセットラインがデフォルトで使用されます。

- sprockets
- webpacker

アセットパイプラインについては、別記事で詳しく書いていますので[こちら]()を参照してください。

##sprocketsって？
railsに昔から導入されているアセットパイプラインです。rails には、Node.jsやモジュールの特徴を持ったCommonJS、AMD、EcmaScript modulesが生まれるより以前の2007年から導入されています。新たな言語の特徴（モジュール）、ツールやbrowser capabilitiesなどの発達に対応はしていませんが、rails向けに開発されており、sprockets-rails gemによって実装することができ、とてもシンプルにアセットをコンパイルできます。
##webpackerって？
rails5.1から導入されたアセットパイプラインです。
Sprocketsとは異なり、Javascriptのモジュール概念に完全に取り込んでいます。
babel、PostCSS, そして最新のwebフレームワークと統合されており、たくさんのモジュール構文に対応しています。
また、設定オプションも多数提供されており、カスタマイズがしやすいのも特徴です。

それでは、両者を比較していきます。
##WebpackerがSprocketsを比較
railsでは、デフォルトで以下のように２つのアセットパイプラインを使い分ける設定になっています。

| webpacker | sprockets|
|:-:|:-:|
|  Javascript | CSSと静的アセット  |

実際、webpackerの設定が書かれているコードを確認すると、、、

~~~ruby:config/webpacker.yml
extract_css: false
~~~
このように設定されていて、WebpackがCSS packをstylesheet_pack_tagで提供する方法を認識していたとしても提供をオフにするようになっています。

それでは、[Railsガイド webpackerの概略]( https://railsguides.jp/webpacker.html)のページで確認していきましょう。
>SprocketsもWebpackerと同様のアセットパッケージングツールで、Webpackerと機能が重複しています。どちらのツールも、JavaScriptをブラウザに適したファイルにコンパイルすることでproduction環境でのminifyやフィンガープリント追加を行えます。development環境では、SprocketsもWebpackerもファイルをインクリメンタルに変更できます。

機能的には、同じようですね。
さらに読み進めていくと、具体的な使い分けの指針が書かれています。

>SprocketsはRailsで使われる前提で設計されているため、統合方法はWebpackerよりもシンプルで、Ruby gemを用いてSprocketsにコードを追加できます。webpackは、より新しいJavaScriptツールやNPMパッケージとの統合に優れており、より多くのものを統合できます。新しいRailsアプリは「JavaScriptはwebpackで管理する」「CSSはSprocketsで管理する」設定になっていますが、webpackでCSSを管理することもできます。

>新しいプロジェクトで「NPMパッケージを使いたい場合」「最新のJavaScript機能やツールにアクセスしたい場合」は、Sprocketsではなくwebpackerを選択すべきでしょう。「移行にコストがかかるレガシーアプリケーション」「gemで統合したい場合」「パッケージ化するコードの量が非常に少ない場合」は、WebpackerではなくSprocketsを選ぶべきでしょう。

| | sprockets | webpacker|
|:-:|:-:|:-:|
| メリット  |  統合がシンプル（ruby gemで統合可能） |  より新しいJavaScriptツールやNPMパッケージとの統合に優れており、より多くのものを統合できる。  |
|  デメリット | より新しいJavaScriptツールやNPMパッケージに対応していない。 |  統合方法がsprocketsほどシンプルではない。 あらゆるものをJavascriptモジュールとして扱うため、CSSや静的アセットについて統合する際は、違和感がある（後述）|

##どうして、rails6では、Webpacker と Sprocketsが共存しているのだろう？
ここまでで、webpckerの方が、今風の技術に使えるのなら、CSSもそうすればいいのでは？と思ったので、もう少し詳しく調べてみました。すると[こちらの](https://rossta.net/blog/why-does-rails-install-both-webpacker-and-sprockets.html)記事に出会いました。

記事の中で紹介されていますが、DHHは、自身のtwitterでこのように発言しています。
>Rails 5.1 will ship with Yarn to manage JS dependencies and --webpack to compile app-like JS via Webpacker:
We will continue to use the asset pipeline for JavaScript sprinkles, CSS, images, and other static stuff. The two approaches coexist great.

webpackerとSprocketsの二つのアセットパイプラインは、うまく共存すると発言しています。これについて、githubのプルリクエスト時に以下のようなやりとりがあります。
>@dwightwatson Out of curiousity, what is the argument to continue using Sprockets for CSS/static assets when Webpacker supports them by default out of the box?
@dhh Webpack’s support is awkward in my opinion and does not offer any benefits over Sprockets. Unlike in the realm of JavaScript compilation.

なぜ、CSSや静的アセットに対してSprocketsをデフォルトとしているのかという質問に対して、DHHは、webpackのサポートは、ぎこちなさがあり、Sprocketsに置き換わるような利点がないと回答しています。

###何がぎこちなく感じるのか。
DHHは、javascript以外のアセットをwebpackerでバンドルすることに対してぎこちなさを感じると発言しています。webpackの何がぎこちないのでしょうか。
####理由：CSSや静的ファイルをjavascriptモジュールのように扱うから
CSSやimageをwebpackを使ってバンドルする場合、それらをjavascriptファイルからインポートしてくる必要があります。

~~~css
import '../application.css'

import myImageUrl from '../images/my-image.jpg'
~~~
どうして、このようにするのかというと、webpackはあらゆるものをjavascriptのモジュールとして扱うからです。
webpackでは、Javascriptからインポートするものは、すべてjavascriptオブジェクトとして扱います。これが、sprocketsを使用していたrailsエンジニアからすると、違和感を感じます。これが、DHHがぎこちないと発言した理由です。

##結局どのように使い分ければいいの？
確かに、CSSや静的アセットをJavascrioptモジュールのように扱うというのは、違和感がありますね。
一方で、rubyが全てのものをオブジェクトと扱うというマインドセットが定着したように、このマインドセットも今後定着する可能があります。自身の状況に応じて、使いわけが必要になります。

[Choosing Sprockets or Webpacker](https://github.com/reactjs/react-rails/wiki/Choosing-Sprockets-or-Webpacker)で、以下のように紹介されていました。

###Sprocketsを選ぶ理由

- RailsアプリがJavaScriptをあまり必要としていない
- グローバルスクリプトとjQueryプラグインでの拡張が好きで、適切なJavaScriptモジュールシステムは必要ない
- レガシーなRailsアプリをWebpackerにアップグレードするとコストがかかりすぎる
- ローカル開発なので高度なツールは必要ない
- Sprocketsで上手くいくし、代替案を増やす時間がない
- Railsアプリが特定のアセットgemに依存しているので、NPMを選択できない

###Sprocketsを選ばない理由

- ローカル開発が遅くなる
- アセットのコンパイルをより詳細に制御する必要がある
- Railsアプリに多くのJavaScriptがあり、大量のペイロードを回避するためにコードの分割機能が必要
- 長期的なサポートが心配

###Webpackerを選ぶ理由

- 適切なJavaScriptモジュールシステムを使用して依存関係を管理したい。つまり、グローバルスコープの汚染を制限し、import/exportとrequireで明示的な依存関係グラフを作成したい
- ES6以上、Babel、PostCSSの最新機能を活用したい
- 動的インポートやwebpackのsplitChunks最適化などのインテリジェントなコード分割機能が欲しい
- ビルドシステムがソースマップを生成する方法に柔軟性を持たせたい
- ホットモジュールリプレイスメント（HMR）など、ローカル開発用の高度なツールが欲しい
- シングルページアプリを構築したい*
- webpackを使用するためにシングルページアプリを用意する必要はありません。「マルチページアプリ」でも非常にうまく機能します。

###Webpackerを選ばない理由

- RailsアプリがJavaScriptをあまり必要としない
- 自分はバックエンド開発者でJavaScriptエコシステムの知識が限られている
- WebpackとWebpackerを理解するための時間を費やす準備ができていない
- 複雑すぎるようにみえる

###両方を使う理由

- 「Rails的な方法」が良い。WebpackerでJavaScriptをコンパイルし、SprocketsでCSS、画像、フォントを扱う
- SprocketsからWebpackerに段階的にアップグレードしたい

##まとめ
- rails５.１以降、アセットパイプラインが２つ存在するの理由は、webpackerの特徴的なアセットの扱い方にある。（全てをJavascriptのモジュールのように扱う。）
- 自身が開発しているアプリケーションの状態に合わせて、使いわけをする。

##参考
[アセットパイプラインについて](https://railsguides.jp/asset_pipeline.html)
[Webpacker の概要](https://railsguides.jp/webpacker.html)
[Rails 6: Webpacker+Yarn+Sprocketsを十分理解してJavaScriptを書く: 前編（翻訳）](https://techracho.bpsinc.jp/hachi8833/2021_05_13/85940)
[Why does Rails 6 include both Webpacker and Sprockets?](https://rossta.net/blog/why-does-rails-install-both-webpacker-and-sprockets.html)
[Choosing Sprockets or Webpacker](https://github.com/reactjs/react-rails/wiki/Choosing-Sprockets-or-Webpacker)