Rails5.1でVue.jsで単一ファイルコンポーネントのエラーがでる（You are using the runtime-only build of Vue where the template compiler is not available）


Vue.jsには、「単一ファイルコンポーネント」という機能があり、一ファイルにhtml,js,cssがまとめられるようになっています。(拡張子が.vueのものがそう）

Rails5.1でVueを設定すると単一ファイルコンポーネントで書かれたファイルが生成される。

```
app/javascript/
└── packs
    ├── app.vue
    ├── application.js
    └── hello_vue.js
```


```html:app.vue
<template>
  <div id="app">
    <p>{{ message }}</p>
  </div>
</template>

<script>
export default {
  data: function () {
    return {
      message: "Hello Vue!"
    }
  }
}
</script>

<style scoped>
p {
  font-size: 2em;
  text-align: center;
}
</style>
```


```hello_vue.js

import Vue from 'vue'
import App from './app.vue'

document.addEventListener('DOMContentLoaded', () => {
  document.body.appendChild(document.createElement('hello'))
  const app = new Vue(App).$mount('hello')

  console.log(app)
})
```


## You are using the runtime-only build of Vue...のようなエラーがでる

「Rails5.1でVue.jsの勉強でも初めてみるか！」とVue.jsを触ったことがない人がやろうと思うと、間違いなくハマるポイントがあって挫折する人もいるかもしれない。

公式のチュートリアル通りに以下のようなコードをブラウザで実行しようとするとエラーになる。

公式
https://jp.vuejs.org/v2/guide/

```hello_vue.js
Vue.component('todo-item', {
  props: ['todo'],
  template: '<li>{{ todo.text }}</li>'
})
var app7 = new Vue({
  el: '#app-7',
  data: {
    groceryList: [
      { id: 0, text: 'Vegetables' },
      { id: 1, text: 'Cheese' },
      { id: 2, text: 'Whatever else humans are supposed to eat' }
    ]
  }
})
```

```index.html.erb
<div id="app-7">
  <ol>
    <todo-item v-for="item in groceryList" v-bind:todo="item"></todo-item>
  </ol>
</div>
```

```
// 上記のファイルをブラウザで確認しようとする

$ bin/webpack-dev-server

You are using the runtime-only build of Vue where the template compiler is not available. Either pre-compile the templates into render functions, or use the compiler-included build
```


これの解決方法だけ先に書いておくと、`config/webpack/shared.js`にaliasを追加すれば解決する。
（公式：https://jp.vuejs.org/v2/guide/installation.html#ランタイム-コンパイラとランタイム限定の違い）


```js
  resolve: {

	// aliasを追加
    alias: {
      'vue$': 'vue/dist/vue.esm.js'
    },

    extensions: settings.extensions,
    modules: [
      resolve(settings.source_path),
      'node_modules'
    ]
  },
```

これで再び、サーバを起動すれば先ほどのエラーはでないはず。

ここからは上記のエラーがなぜ起きるのか簡単に書いていきたいと思います。
解決方法だけ知りたかった人はここまでで大丈夫です。


## 単一ファイルコンポーネントとコンパイラとランタイム


Vueを昔から触っている人だと、1.X系にもある以下のやりかたがわかりやすいと思う。

```index.js
Vue.component('index', {
  template: '<p>Hello World</p>'
})
```

もしくは別でテンプレートを用意するやり方もあると思う

```index.js
 Vue.extend('index', {
  template: '#home_index'
})
```

```index.html
<script type="text/vuejs+html" id="home_index">
</script>
```

Vue.jsの大きな書き方として、`template:`で指定する方法と、単一ファイルコンポーネントを使用する方法がある。どちらもコンパイルする必要があるのだけれど、コンパイルされるタイミングが違う。

単一ファイルコンポーネントをつかっている場合は、すぐに実行できる形にコンパイルされるのだが、`template:`となっている方は、ブラウザで実行されたときにJITコンパイラが走らないといけない。そのためには完全ビルドする必要がある。

しかし、Vue.jsではデフォルトでは完全ビルドは「やらない」設定になっている。そのため、先ほどのようなエラーがでるようになっている。`template:`はコンパイルできないよーと言われているわけだ。

だから設定を変えてあげて、完全ビルドしてくれる設定にするわけですね。これ初回だとけっこう詰まるところだと思う。

なんで最初からその設定をしておかないんだと言うと、完全ビルドよりも最初の設定の方が30%軽量なんですね。だから公式は初期設定をデフォルトにしているわけですね。


```js
  resolve: {

	// aliasを追加
    alias: {
      'vue$': 'vue/dist/vue.esm.js'
    },

    extensions: settings.extensions,
    modules: [
      resolve(settings.source_path),
      'node_modules'
    ]
  },
```

なので、もし単一ファイルコンポーネントではなく、Vueの1.xなどと同じ書き方がしたい人は、上記のように完全ビルドするような設定を追加してください


## 参考

公式
https://jp.vuejs.org/v2/guide/installation.html#さまざまなビルドについて

Vue2.x系のハマりどころ templateとコンパイラを完全解説するよ
https://aloerina01.github.io/javascript/vue/2017/03/08/1.html