Rails+WebpackerでVue.jsとTurbolinksを同時に動かす

# これは何

RailsでVue.jsとTurbolinks動かしていい感じのフロントエンド環境を構築しよう！！！

一応それぞれのざっくりとした説明。

- Rails
    - みんな大好きWebフレームワーク
- Webpacker
    - Railsで書いたJSやCSSをよしなにまとめてくれるやつ
- Vue.js
    - 言わずと知れたJSのフロントエンドフレームワーク
- Turbolinks
    - ページ移動を速くしてくれるやつ

# リポジトリとバージョン情報

リポジトリ: https://github.com/rhistoba/rails_vuejs_turbolinks_template

- Ruby: 2.6.3
- Rails: 5.2.3
- Webpacker: 4.0.7
- Vue.js: 2.6.10
- Turbolinks: 5.2.0

# どういう感じにVue.jsを使えるようにするのか

1. Railsアクションからビューをレンダリング
2. `turbolinks:load`時にビューからVueインスンタンスのid要素を検索
3. id要素が見つかればVueインスタンスを生成
4. ビューのid要素以下をテンプレートとしてVueインスタンスが適用される

こんな感じで、ビューでidを指定した箇所にVueを適用するための方法を説明します。
部分的にVueを適用可能なので、いわゆる薄い使い方によりRails Wayから外れない開発が可能かと思います。

# 手順

今回は適当に`rails new`してルートのビューだけ作成したRailsプロジェクトを対象に説明します。

## Webpackerを導入

GemfileにWebpackerを追加。

```ruby:Gemfile
# ...
gem 'webpacker', '~> 4.x'
# ...
```

追加したら`bundle install`。

```
$ bundle install
```

以下のコマンドでプロジェクトにWebpackerをインストールする。

```
$ bundle exec rails webpacker:install
```

## Vue.jsを導入

以下のコマンドでプロジェクトにVue.jsを追加する。

```
$ bundle exec rails webpacker:install:vue
```

## Turbolinksなどの導入

yarnで以下のようにパッケージを取得する。

```
$ yarn add turbolinks vue-turbolinks
```

`javascript/packs/application.js`に以下を追加する。

```js:javascript/packs/application.js
import Turbolinks from 'turbolinks'

Turbolinks.start()
```

`views/layouts/application.html.erb`で以下の一文を追加する。

```erb:views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    ...
+   <%= javascript_pack_tag 'application' %>
  </head>
  ...
</html>
```

これでWebpackerにより`javascripts/packs/application.js`がビルドされるようになる。

## Vue.jsで完全ビルドを有効にする

ビューから取得したid要素のhtmlを、Vueインスタンスにテンプレートとして渡してコンパイルされる必要があるのですが、Vueは標準でランタイム限定ビルドのみ有効になっており、このままでは意図通りに動かせません。
参考: https://jp.vuejs.org/v2/guide/installation.html#さまざまなビルドについて

そのため参考URLのページにも載っているように、Webpackの設定で完全ビルドを有効にします。

`config/webpack`に`custom.js`を作成します。

```js:config/webpack/custom.js
module.exports = {
  resolve: {
    alias: {
      'vue$': 'vue/dist/vue.esm.js'
    }
  }
}
```

`config/webpack/environment.js`にて上記の設定を取り込むようにします。

```js:config/webpack/environment.js
// ...
const customConfig = require('./custom')
environment.config.merge(customConfig)

module.exports = environment
```

これでVueの完全ビルドが有効になります。

## src/main.jsを作成

`javascripts/packs/application.js`で各種Vueインスタンスを読み込む大元のファイルを作成しましょう。

`javascripts`以下に`src`ディレクトリを作成します。

```
mkdir javascripts/src
```

`javascripts/src`以下に`main.js`を以下の内容で作成します。

```js:main.js
import Vue from 'vue'
import TurbolinksAdapter from 'vue-turbolinks'

Vue.use(TurbolinksAdapter)
```

## 実際に使う

準備ができたので、実際にVueインスタンスを作成して動かします。

適当なビュー（今回は`views/home/index.html.erb`）でVueインスタンスで使われるid要素を追加して、その要素以下でVueのテンプレート表記でhtmlを記述します。

```erb:views/home/index.html.erb
<div id="vue-app">
  {{message}}
</div>
```

`javascripts/src`以下にVueインスタンス作成のファイルを書きます。

```js:javascripts/src/app.js
import Vue from 'vue'

document.addEventListener('turbolinks:load', () => {
  const el = document.getElementById('vue-app')
  if (el) {
    new Vue({
      el: el,
      data() {
        return {
          message: 'Hello Vue!'
        }
      }
    })
  }
})
```

最後に`javascripts/src/main.js`で上記のファイルを`import`するよう追記します。

```js:javascripts/src/main.js
import Vue from 'vue'
import TurbolinksAdapter from 'vue-turbolinks'

Vue.use(TurbolinksAdapter)

+ import './app.js'
```

`rails s`してブラウザで以下のように確認できれば、完了です。

<img width="327" alt="RailsVueTemplate.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254436/4600bdf2-1a59-2036-2575-0b3c36dd1ee1.png">

# おしまい

RailsでのVueライフをごゆるりと…