Rails + Vue + Typescript な環境を構築してみる。Part1

  • Vue
  • Typescript
  • Rails
  • 備忘録

Rails プロジェクトを新規作成して、Vue を導入する

Rails で Vue を使いたい場合、Rails プロジェクトを新規作成する時点でオプションを付けて rails new [プロジェクト名] --webpack=vue を実行すれば OK です。

今回は、既存の Rails プロジェクトに後から Vue を導入するケースを想定して、rails new 後に bin/rails webpacker:install:vue で Vue を導入します。

$ rails new vue-ts-rails-sample-app
$ bin/rails webpacker:install:vue

これで Webpacker の基本的な設定が済んだ状態で Vue がインストールされて、Rails で Vue が使えるようになる。

Typescript を導入する

続いて、TypeScript の導入を行っていきます。

$ bin/rails webpacker:install:typescript

Typescript with Vue Components を参考に、config/webpack/loaders/typescript.js を修正し ts-loader の対象に .vue を追加します。

// config/webpack/loaders/typescript.js
const PnpWebpackPlugin = require('pnp-webpack-plugin')

module.exports = {
  test: /\.(ts|tsx)?(\.erb)?$/,
  use: [
    {
      loader: 'ts-loader',
      options: PnpWebpackPlugin.tsLoaderOptions({
        appendTsSuffixTo: [/\.vue$/]
      })
    }
  ]
}

.vue をコンパイルする際に Vue の型定義ファイルが必要になるため、app/javascript 以下に型定義ファイルを追加します。今回は app/javascript/types/vue.d.ts としました。
*.d.ts の設置場所は tsconfig.jsonpaths に指定されているパス以下ならどこでもいい。

// app/javascript/types/vue.d.ts
declare module '*.vue' {
  import Vue from 'vue'
  export default Vue
}

.vue 内などで @/* と書いた時に app/javascript/ 以下が参照されるようにエイリアスを設定します。

// config/webpack/environment.js
const { resolve } = require('path')
// ...snip...
environment.config.merge({
  resolve: {
    alias: {
      '@': resolve('app/javascript')
    }
  }
})
// ...snip...
// tsconfig.json
"paths": {
    "*": ["node_modules/*", "app/javascript/*"],
+   "@/*": ["app/javascript/*"]
},

一先ずここまで行ったところで、Rails への Vue + Typescript の導入が正しくできているか確認してみます。

Vue、Typescript のインストール時に生成された hello_vue.jshello_typescript.ts をリネーム(or 削除)して、v_page.ts を作成します。(ここら辺は好きなようにしてどうぞ。)

// app/javascript/packs/v_page.ts
import Vue from 'vue'
import App from '@/app.vue'

document.addEventListener('DOMContentLoaded', () => {
  const container = document.querySelector("#v-page")
  if (container === null) return

  const app = new Vue({
    render: h => h(App)
  }).$mount()
  container.appendChild(app.$el)
})

/ で Vue を表示するために controller、routes、view を変更(もしくは新規作成)します。

# app/controllers/sample_controller.rb
class SampleController < ApplicationController
  def index
  end
end
# config/routes.rb
Rails.application.routes.draw do
  root to: "sample#index"
  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end
<!-- app/views/sample/index.html.erb -->
<div id="v-page"></div>
<%= javascript_pack_tag 'v_page', 'data-turbolinks-track': 'reload' %>

app.vuescriptlang="ts" に変更して、Typescript に対応した書き方に修正します。
今回はクラスベース API は使用せずに、Vue.extend() を使った書き方にしました。

<!-- app/javascript/app.vue -->
<template>
  <div id="app">
    <p>{{ message }}</p>
  </div>
</template>

<script lang="ts">
import Vue from 'vue'

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

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

ここまで終わったら、Rails サーバを起動して / にブラウザからアクセスし、正常にコンパイルができていることと、想定通りの表示になっていることを確認できれば OK です。

おまけ

foreman を導入する

Gemfile に以下を追記後、bundle install

# Gemfile

# foreman
gem 'foreman'
$ bundle install

Procfile.dev を作成して、以下を追記。

# Procfile.dev
app: bin/rails s -b 0.0.0.0
frontend: bin/webpack-dev-server

bin/dev-server を作成して、以下を追記。パーミッションを 755 に。

# bin/dev-server
bundle exec foreman start -f Procfile.dev
$ chmod 755 bin/dev-server

これで bin/dev-server を実行すると、foreman 経由で Rails サーバが立ち上がり、http://localhost:5000/ でアクセスできるようになる。

$ bin/dev-server

Pug を導入する

個人的には Vue の SCF の template は Pug で記述したいので、Pug を導入します。

$ yarn add pug pug-plain-loader
// config/webpack/loaders/pug.js
module.exports = {
  test: /\.pug$/,
  use: [
    {
      loader: 'pug-plain-loader'
    }
  ]
}
// config/webpack/environment.js
const pug = require('./loaders/pug')

environment.loaders.prepend('pug', pug)
Last Updated: 9/30/2019, 4:29:57 PM