Rails + Vue で単一コンポーネントファイルを使う

Railsプロジェクトの作成とVueのインストールは省略。


/app/views/layouts/application.html.erb

  <body>
    <div id="potatoSNS">
      <%= yield %>
    </div>
      <%= javascript_pack_tag 'main' %>
      <%= stylesheet_pack_tag 'main' %> 
  </body>

bodyの中にidを付けたdiv要素を作り、javascript_pack_tagとstylesheet_pack_tagを読み込む。
今回はmain.jsというファイルを読み込んでいるが、このファイルがRailsとVueの接点というか、Vue側のファイルの呼び出し口になる。


/app/javascript/packs/main.js

import Vue from 'vue/dist/vue.esm'
import App from '../app.vue'
import router from './router'


 document.addEventListener('DOMContentLoaded', () => {
   const app = new Vue({
     el: '#potatoSNS',
     router: router,
     render: h => h(App)
   })
 })

そのmain.jsファイルでVueインスタンスを生成し、idをつけた要素をマウントする。
render: h =>(App)はimportしたapp.vueファイルを読み込んでいる。 このapp.vueのなかでコンポーネントを登録したり、router-viewを呼び出したりする。


/app/javascript/app.vue

<template>
  <div id="app"><!--これがホーム-->
      <main-header></main-header> // コンポーネントを呼び出す
      <div class="contents">
        <router-view /> //ルーティングされたファイルが差し込まれる
      </div>
      <modal-window v-show="$store.state.isModalOpen"></modal-window> // コンポーネントを呼び出す
  </div>
</template>

<script>
import Vue from 'vue/dist/vue.esm'
import mainHeader from './components/mainHeader.vue' //コンポーネントを呼び出すにはこうしてimportする必要がある
import modalWindow from './components/modalWindow.vue'

export default {
  components: {  //  コンポーネントを登録
    'modal-window': modalWindow,
    'main-header': mainHeader
  }  
}
</script>

こんな感じです。
<router-view /> //ルーティングされたファイルが差し込まれるとありますが、ではルーティングはどこで行われるのかというと
さっきの/app/javascript/packs/main.js

import Vue from 'vue/dist/vue.esm'
import App from '../app.vue'
import router from './router' //router.jsをimport


 document.addEventListener('DOMContentLoaded', () => {
   const app = new Vue({
     el: '#potatoSNS',
     router: router,  //ここで適用している
     render: h => h(App)
   })
 })


router.jsはというと、 /app/javascript/packs/router.js

import Vue from 'vue/dist/vue.esm'
import Router from 'vue-router' // vue-routerをimport

import MainContents from '../components/mainContents.vue' // 各コンポーネントをimportする
import Top from '../top.vue'
import Signup from '../Signup'
import Signin from '../Signin'
import Registration from '../Registration'

Vue.use(Router)

let router = new Router({
  routes: [
    {
      path: '/',
      // nameは名前付きルートのときに使用される
      name: 'home',
      component: MainContents, // importしたものをここで適用
      meta: { requiresAuth: true }
    },
    {
      path: '/top',
      name: 'top',
      component: Top
    },
    {
      path: '/signup',
      name: 'Signup',
      component: Signup
    },
    {
      path: '/signin',
      name: 'Signin',
      component: Signin
    },
    {
      path: '/registration',
      name: 'Registration',
      component: Registration
    }
  ]
})

export default router

作成しておいたvueファイルをインポートして、new Routerインスタンスの中でcomponentの値として呼び出すことで、さきほどの<router-view />の部分にページが差し込まれるということです。

ちなみにvue-routerもnpmでインストールする必要がありますが、その際

npm install vue-router --save

というように --save をつけるとpackage.jsonに自動で追加されるので、デプロイしたときにvue-routerが認識されない!なぜだ!なんてことになって数時間無駄にせずに済みます。私はしました。

それと、import Vue from 'vue/dist/vue.esm'は何度も出てきていますが、これがvueになってたりVueになってたりと表記揺れがあるとどっかでエラーが出るので注意しましょう。




main.jsではvueファイルの大元になるapp.vueやルーティング、またVuexのストアを読み込み、app.vueでは各コンポーネントを登録してtemplateで呼び出しをする。というように全体を管理するファイルがありつつ役割ごとにファイルを分けられるので分かりやすくて良いですね。

とくに単一ファイルコンポーネントは、style scopedでコンポーネントごとにcssを管理できるのがかなりありがたいです。templateと同じファイル内に該当箇所のcssがあるのはとても楽だった。