Webpackを知る

# なぜ書くのか？
先日、railsのViewで謎の`padding-top:60px;`に出くわした。検証ツールでみるに、`reboot.css`の46行目と書いているが、`reboot.css`の中をみてもない。自分のファイルにもそんな記述はない。どうして？色々模索し、先輩エンジニアさんに質問させていただいたところ、以前のscssファイルに書いた`padding-top:60px;`が残り、使われていることがわかった。つまり、現在の最新の状態のscssが読み込まれていなかったそう。どうして、読み込まれていなかったのか？それは、どうやら問題はwebpackにあるようである。

# Webpackとは？
Webアプリケーションの装飾に必要なCSS、JavaScript、画像などを１つのファイルとしてまとめるためのモジュールバンドラーで、node.jsのモジュールの1つ。従来、使用するjavascriptファイルを個別で指定していために、表示に時間がかかったり、ファイルの指定を手動で行うために、ミスが発生しやすかった。その問題を解決したのがWebpack。対象のファイルを自動で検出し追加してくれるため、手動によるミスの発生や不適切な順序でファイルが実行されてしまう心配がなくなり、また、時間の高速化にもつながった。

## Webpackerとは
WebpackerとはWebpackをRuby on Railsで使うためのgemパッケージ。webpackを良い感じに動かしてくれる優れもの。

## Webpackメリットを整理

### 依存関係のあるJavaScriptのモジュールを解決
ファイル間の依存関係を解析して1つのファイルにまとめてくれるので、保守性が高くなり他のプロジェクトにもファイルを転用しやすくなる。

### ファイル読み込みが効率化
ブラウザとウェブサーバーの同時接続数が限られるため、複数のファイルがあると転送に時間がかかる。ファイルを一つにまとめることで、リクエスト回数を減らすことができ、サーバーの負担が少なくなり高速化することができる。

### CSSや画像をJavaScriptで扱う
ローダを使えば、HTMLやCSSファイルなど、JavaScript以外のファイルをJavaScriptで扱うことができます。また、画像をDataURLに変換処理したり、コードをチェックしたりすることもできる。

## http2とwebpackの関係
HTTP/1.1では同一のホスト名に対して同時にリクエストできる数には制限があるために、複数のスクリプトファイルを[webpack](https://webpack.js.org/)や[Browserify](http://browserify.org/)といったバンドルツールを用いて結合させるのが一般的だった。HTTP/2ではリクエストとレスポンスのストリームが多重化されている。並列して複数のリクエストを送信できるようになったため、これまで当たり前のことであったスクリプトファイルの結合の必要性は薄くなった。しかしながら、http2に置いても二つの点からwebpackを使用するメリットがある

### ストリーム数には制限がある
ほとんどのサーバーでは、接続時に送信されるSETTINGSフレームの中で、同時ストリーム数を100などの妥当な値に制限している。これは、100以上の同時リクエストを発行できないことを意味しており、例えば、数百のjsファイルを持つ大規模なアンバンドルReactアプリを持っている場合には問題となる

### js間の依存関係をバンドルする
javascript ファイル間には推移的な依存関係があり、http2のようにすべての依存関係をバンドルしていない場合、ブラウザは以前のレスポンスを受信したときにのみ依存関係を発見するため、多くのリクエストラウンドトリップが必要となる。

これらの理由から、webpack を使用していくつかの同種のバンドルをパッケージ化し、最大同時リクエスト数がサーバーの制限を超えないようにする一方で、効率的なブラウザキャッシングを活用するためにバンドルの粒度を十分に保つ。
[参考記事](https://stackoverflow.com/questions/37556283/what-is-the-value-of-using-webpack-with-http-2)

## webpackerをRailsアプリケーションに導入
rails6以降は、標準でwebpackerが導入されているが、それ以前のverの場合の既存のアプリには`bin/rails webpacker:install`を使用する。またwebpackerは、yarnに依存するために、yarnを事前にインストールする。Webpackerをインストールすると、デフォルトでは`app/javascripts/packs`ディレクトリ以下のJavaScriptファイルがコンパイルされて独自のpackファイルにまとめられる。

例えば私の場合、`app/javascript/packs/application.js`というファイルができ、Webpackerはapplicationという名前のpackを作成する。そしてこのpackは、`<%= javascript_pack_tag "application" %>`というERBコードで指定し使用する。またすべてのERBで、webpackerの機能を使いたいので、`application.html.erb`の headタグ内に記載する。

**CSSや画像の場合**

What | Webpacker
--------- | ---------
JavaScriptをアタッチする	 | javascript_pack_tag
CSSをアタッチする | stylesheet_pack_tag
画像にリンクする | image_pack_tag

### css フレームワークを使う
```
1. application.html.erbの headタグ内に、<%= stylesheet_pack_tag "application" %>、を追加
2. yarn add <フレームワーク名>
```

### railsアプリケーションにbooststrapを導入
1. `yarn add jquery bootstrap popper.js`

2. 必要なモジュールを、グローバル変数に代入する

```js:config/webpack/environment.js
// jQueryとBootstapのJSを使えるように
const webpack = require('webpack')
environment.plugins.prepend(
  'Provide',
  new webpack.ProvidePlugin({
    $: 'jquery',
    jQuery: 'jquery',
    Popper: 'popper.js'
  })
)
```


3.  bootstrapをimport

```sass:app/javascript/stylesheets/application.scss
@import '~bootstrap/scss/bootstrap';
```

`import 'bootstrap'`とせずに、必要に応じて個別にプラグインをインポートすることで、無駄を削除

4.bootstrapと、application.scssをimport

``` js:app/javascript/packs/application.js
import 'bootstrap';
import '../stylesheets/application';
```


### webpackのエントリーファイルだけを置く
ここで重要なのが、`app/javascript/packs`ディレクトリにはwebpackのエントリーファイルだけを置き、それ以外のものを置かないことが重要。application.*だけ

```
app/javascript:
  ├── packs:
  │   # ここにはwebpackエントリーファイルだけを置くこと
  │   └── application.js
  │   └── application.css
  └── src:
  │   └──〇〇.js
  └── stylesheets:
  │   └── ▲▲.css
  └── images:
      └── ■■.png
```

### 構成を変更したい場合
構成を変更したい場合は、config/webpacker.ymlを書き換える。config/webpacker.ymlには、エントリーポイントやdevelopやproduction固有の設定を定義している。

```config.yml
default: &default
  source_path: app/javascript # pathを書き換える、以下も同様に
  source_entry_path: packs
  public_root_path: public
  public_output_path: packs
  cache_path: tmp/cache/webpacker
  webpack_compile_output: true
  ...続く
```

## ビルドしてみる
`bin/webpack`を実行

```
% bin/webpack
Hash: 5e57f23ab887a258f576
Version: webpack 4.46.0
Time: 5572ms
Built at: 2021-05-01 17:17:56
                                     Asset       Size       Chunks                         Chunk Names
    js/application-7cad38888ab4f4a77a4e.js   1.27 MiB  application  [emitted] [immutable]  application
js/application-7cad38888ab4f4a77a4e.js.map   1.39 MiB  application  [emitted] [dev]        application
                             manifest.json  364 bytes               [emitted]              
```

app/javascript/packs/application.jsがビルドされて public/packs/ に application.jsとapplication.js.mapというファイルが生成される

### .mapて？
.map拡張子はSourceMapといい、コンパイル前とコンパイル後の対応関係を記したファイルである。Json形式で記述されている。ブラウザーが元のソースを再構成して、そのソースをデバッガーに提供することで、デバックをしやすくするらしい。

`bin/webpack`でビルドしたjsをインクルードするために、**`javascript_pack_tag`** を使用する。これで、なんとなく流れは理解。

### さらに便利にビルドする
`bin/webpack-dev-server`このコマンドと`rails s`をしてサーバーを動かすと、JS側の変更をホットリロードしてくれるので開発が快適に！下記のようになるので、成功したかもわかりやすい。
```成功時
<中略>
ℹ ｢wdm｣: Compiling...
ℹ ｢wdm｣: Compiled successfully.
```

```失敗時 
# :を抜いた
ERROR
>>     padding-top 60px;
   ----^
```

## 最後に
` padding-top: 60px;`を消したい！という気持ちから、多くのことを学べた。今までの自分なら、絶対に `padding-top: 0px !important;`などと書いたり、あるいは無視したりしていた。近道をしていたら必ずまた同じ問題に遭遇し、その度適当にこなしていたら、どんどん複雑で無駄の多い汚いコードになる。中々課題の進捗はないが、今日もまた昨日より新しい知識を手に入れた。着実に確実に積み上げていきたい。