Docker上のRailsアプリにWebpackerを導入してみる

## はじめに
この記事では、既存のRailsアプリにWebpackerを導入する手順を書いていきます。
WebpackerやWebpackを使用したことがない方向けです。

### 開発環境
Rails 5.2.3
ruby 2.5
mariadb 10.1
docker

## Webpackerとは
Railsでwebpackを使用するためのgemパッケージです。
複数のjs,css,画像などを一つにまとめることにより、
ファイルの取得にかかる時間を短縮できるというメリットがあります。

## 実装
### gemのインストール
まずアプリにgemパッケージをインストールします。

```:Gemfile
gem 'webpacker', '~> 4.x' 
```
Gemfileを更新したら、`bundle install`します。

### yarnのインストール
Webpackerは`yarn`というnode.jsのパッケージマネージャを使用するそうなので、
Dockerにyarnをインストールするためのコマンドを追記します。

```ruby:Dockerfile
RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \
    echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \
    apt-get update && apt-get install yarn
```

### Webpackerの起動

```yml:docker-compose.yml
  webpacker:
    build: .
    environment:
      NODE_ENV: development
      RAILS_ENV: development
      WEBPACKER_DEV_SERVER_HOST: 0.0.0.0
    command: ./bin/webpack-dev-server
    ports:
      - '3035:3035'
```
アプリ用のコンテナにWebpackerホスト名の環境変数を追加します。

```yml:docker-compose.yml
  web:
    build:
      context: .
      dockerfile: Dockerfile
    command: /bin/sh -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    tty: true
    stdin_open: true
    depends_on:
      - db
    ports:
      - '3000:3000'
    volumes:
      - .:/usr/src/app
  # 以下を追記
    environment:
      WEBPACKER_DEV_SERVER_HOST: webpacker
```
これで`docker-compose up --build`をすると、Webpackerが起動してくれます。
このあたりは、[公式ドキュメント](https://github.com/rails/webpacker/blob/master/docs/docker.md)にわかりやすく記載されています。

## 基本的な使い方
Webpackerをインストールすると、`config/webpacker.yml`が作成されます

```yml:config/webpacker.yml
default: &default
  source_path: app/javascript
  source_entry_path: packs # この配下のファイルをエントリーポイントとして読み込む
  ...
```
```:構成
app/javascript:
  ├── packs:
  │   # ここに置いたファイルが読み込まれる
  │   └── application.js
  └── javascripts:
  │   └── application.js
  └── stylesheets:
      └── application.scss
```
`javascripts/application.js`, `stylesheets/application.css`を
アプリケーション全体に適用させたい場合は以下のように書きます。

```js:packs/application.js
import '../javascripts/application.js'
import '../stylesheets/application.scss'
```

```:app/views/layouts/application.html.erb
<%= javascript_pack_tag 'applocation' %>
<%= stylesheet_pack_tag 'application' %>
```
これでビューに反映することができます。

ちなみに、今回はWebpackerの実行コマンドに`./bin/webpack-dev-server`を指定しているため、
jsファイルの中身を更新するとブラウザをリロードすることなく自動的に変更を反映してくれます。
ただ、ファイル自体を追加するなど、構成を変更した場合はrailsを再起動する必要があります。

### ページ毎に適用するファイルを分ける場合
```:構成
app/javascript:
  ├── packs:
  │   └── application.js
  └── javascripts:
  │   └── application.js
  │   └── tasks:
  │       └── application.js
  │       # 実際の処理が書かれているjavascriptファイル
  │       └── index.js
  │       └── show.js
  └── stylesheets:
      └── application.scss
```
エントリーポイントである`pakcs`配下にjsファイルを読み込むためのエントリーファイルを作成します。

```:構成
app/javascript:
  ├── packs:
  │   └── application.js
  │   # 追加
  │   └── tasks:
  │       └── index.js
  │       └── show.js
  └── javascripts:
  │   └── application.js
  │   └── tasks:
  │       └── index.js
  │       └── show.js 
  └── stylesheets:
      └── application.scss
```
追加したファイルにインポート文を記述します。

```js:packs/tasks/index.js
import '../../javascripts/tasks/index.js'
```
```js:packs/tasks/show.js
import '../../javascripts/tasks/show.js'
```
それぞれのページでjavascriptを呼び出します。

```ruby:app/views/tasks/index.html.erb
<%= javascript_pack_tag 'tasks/index.js' %>
```
```ruby:app/views/tasks/show.html.erb
<%= javascript_pack_tag 'tasks/show.js' %>
```
## そのほか
### Bootstrap4を適用させる
Webpackerを導入したところ、Bootstrapが効かなくなりました。
こちらのサイトを参考に設定。
https://qiita.com/rhistoba/items/f724dae231d7e28bf477

### confirmを動作させる
Webpackerを導入したところ、confirmダイアログが効かなくなりました。
こちらのサイトを参考に設定。
https://qiita.com/mokuo/items/a50a27a83c3328c116a7

### Github ActionsでWebpackerを動作させる
Github Actionsで用意されているUbuntuにはyarnがデフォルトで入っているようですが、
そのままだとエラーが出てしまいました。
`yarn upgrade`を追記することで動作するようになりました。

```yml:.github/workflows/ruby.yml
    - name: Compile with Webpacker
      run: |
        yarn upgrade
        bundle exec rake webpacker:compile
      env:
        NODE_ENV: test 
```
## 最後に
ご指摘、コメントお待ちしております。