Rails6 + Webpacker環境のVue.js3導入と、Viewからpropsへの受け渡し

環境

※Docker Desktop環境

Vueのインストール

yarn add vue@next vue-loader@next @vue/compiler-sfc

package.json

{
  "name": "myapp",
  "private": true,
  "dependencies": {
    "@rails/actioncable": "^6.0.0",
    "@rails/activestorage": "^6.0.0",
    "@rails/ujs": "^6.0.0",
    "@rails/webpacker": "5.4.3",
    "@vue/compiler-sfc": "^3.2.26", // 追加項目
    "turbolinks": "^5.2.0",
    "vue": "^3.2.26", // 追加項目
    "vue-loader": "^17.0.0", // 追加項目
    "webpack": "^4.46.0",
    "webpack-cli": "^3.3.12"
  },
  "version": "0.1.0",
  "devDependencies": {
    "webpack-dev-server": "^3"
  }
}

config\webpack\environment.jsの編集

初期状態

const { environment } = require('@rails/webpacker')

module.exports = environment

編集後

const { environment } = require('@rails/webpacker')

const path = require('path')
const { VueLoaderPlugin } = require('vue-loader')
const { DefinePlugin } = require('webpack')

// アプリケーションのalias
const customConfig = {
  resolve:{
    alias: {
      "@": path.resolve(__dirname, '..', '..', 'app/javascript')
    }
  }
}

environment.plugins.prepend(
  'VueLoaderPlugin',
  new VueLoaderPlugin()
)

environment.loaders.prepend('vue', {
  test: /\.vue$/,
  use: [{
      loader: 'vue-loader'
  }]
})
environment.plugins.prepend(
  'Define',
  new DefinePlugin({
      __VUE_OPTIONS_API__: false,
      __VUE_PROD_DEVTOOLS__: false
  })
)

environment.config.merge(customConfig)

module.exports = environment

config\webpacker.ymlの編集

- extensions:
  - .vue # 追加

Controller、View、JavaScriptの作成

config\routes.rb

Rails.application.routes.draw do
  resources :users
end

app\controllers\users_controller.rb

class UsersController < ApplicationController
  def index
    @users = [{ id: 1, name: 'hoge' }, { id: 2, name: 'fuga' }]
  end
end

app\views\users\index.html.erb

<div id="user" data-users="<%= @users.to_json %>">
  <ul>
    <li v-for="item in items">
     {{ item.name }}
    </li>
  </ul>
</div>

<%= javascript_pack_tag('users') %>

app\javascript\packs\users.js

import { createApp } from 'vue/dist/vue.esm-browser'
import Index from '@/users/index'

document.addEventListener('DOMContentLoaded', () => {
  // カスタムデータからの値取得と、propsへの受け渡し
  const { users } = document.getElementById('user').dataset
  createApp(Index, { users: JSON.parse(users) }).mount('#user')
})

app\javascript\users\index.js

export default {
  props: {
    users: {
      type: Array,
      default: [],
    }
  },

  data() {
    return {
      items: []
    }
  },

  mounted() {
    this.items = Object.assign({}, this.users)
  }
}