Rails + Vue3 + TypeScriptで新しいプロジェクトを作ってみた
Vue3がリリースされたみたいなのでRailsとvue、tsで新しくプロジェクトを作てみました。
後から気づいたけど、vue/cliを使えばもvue3とTypeScriptを同時インストールできたかも?
コード: https://github.com/tOba1357/vue3_rails/tree/73e679541da8308010fde8eab0da3298b77a6f48
setup rails
rbenv local 2.7.2
Gemfile作成
source 'https://rubygems.org'
gem 'rails', '6.0.3.4'
railsプロジェクト作成
bundle install
bundle exec rails new -d postgresql --api --skip-action-mailer --skip-active-storage --skip-action-cable .
# database.ymlを編集
./bin/rails s
install vue3
参考: https://v3.vuejs.org/guide/installation.html
npm init vite-app frontend
cd frontend
nodenv local 14.15.0
npm install
defaultのportが3000でrailsと被っているのでviteのportを8080に変更とaliasの設定をします。
webpackと違って@...
ができないみたいなので代わりに'/@/...'を使います。https://github.com/vitejs/vite/issues/88
frontend/vite.config.js
const path = require('path')
export default {
port: 8080,
alias: {
'/@/': path.resolve(__dirname, 'src')
}
}
npm run dev
あとは、http://localhost:8080/ にアクセスしてみて表示されればおk
setup TypeScript
参考: https://v3.vuejs.org/guide/typescript-support.html
tsconfig.json作成
// tsconfig.json
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
// this enables stricter inference for data properties on `this`
"strict": true,
"jsx": "preserve",
"moduleResolution": "node",
"baseUrl": ".",
"paths": {
"/@/*": [
"src/*"
]
}
}
}
install TypeScript
npm install --global @vue/cli
vue add typescript
frontend/src/App.vueを編集
<template>
<img alt="Vue logo" src="./assets/logo.png" />
<HelloWorld msg="Hello Vue 3.0 + Vite" />
</template>
<script lang="ts">
import HelloWorld from './components/HelloWorld.vue'
import { defineComponent } from 'vue'
export default defineComponent({
components: {
HelloWorld
}
})
</script>
フロントのサーバをリスタートして http://localhost:8080/ にアクセスして、同じように表示されればおk
vueからRailsApiを叩く
ここでは、ユーザの作成して一覧取得する機能を作成します。
userモデルとcontroller作成
nameだけ持ったuserを作成します。
bin/rails g model user
migration
class CreateUsers < ActiveRecord::Migration[6.0]
def change
create_table :users do |t|
t.string :name, null: false
t.timestamps
end
add_index :users, :name, unique: true
end
end
bin/rails g db:migrate
app/controllers/users_controller.rb
class UsersController < ApplicationController
def index
render json: User.all
end
def create
user = User.new(user_params)
if user.save
render json: user, status: 201
else
render json: user.errors.full_messages, status: 400
end
end
private
def user_params
params.require(:user).permit(:name)
end
end
config/routes.rb
Rails.application.routes.draw do
resources :users
end
作ったAPIをAdvanced REST clientを使って叩いてみる。
user作成
user取得
vueからuser作成、一覧取得
CORSの対応
rack-cors(https://github.com/cyu/rack-cors)というgemを使ってCORSを許可。
Gemfileに
gem 'rack-cors'
を追加、bundle installして config/initializers/cors.rbを下記のよう編集
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins 'localhost:8080'
resource '*',
headers: :any,
methods: [:get, :post, :put, :patch, :delete, :options, :head]
end
end
axiosの設定
api通信するようにaxiosをinstall。
npm install axios
hostが異なるので、hostを設定したaxiosを使うようします。
frontend/src/lib/axios.ts
import axios from 'axios'
export default axios.create({
baseURL: 'http://localhost:3000/',
})
user一覧取得と、作成
ここからは私の理想のtsの構成になっていますので、参考程度にしてください。
Userモデルを作成。
フロントで扱うuserモデルを定義します。
dayjsを使っているのでdayjsをインストールしてください。
frontend/src/models/user.ts
import dayjs, {Dayjs} from 'dayjs';
export default class User {
id: number
name: string
createdAt: Dayjs
updatedAt: Dayjs
constructor(id: number, name: string, createdAt: string, updatedAt: string) {
this.id = id
this.name = name
this.createdAt = dayjs(createdAt)
this.updatedAt = dayjs(updatedAt)
}
}
フロントでrailsのapiを叩く機能を抽出します。
userを作成する時のパラメータの定義と、responseからUserモデルを作成する機能をここで持ちます
frontend/src/apis/users_api.ts
import axios from "/@/lib/axios"
import User from "/@/models/user"
function createUserFromResponse(res: any): User {
return new User(res.id, res.name, res.created_at, res.updated_at)
}
export const getUsers: () => Promise<User[]> = async () => {
const res = await axios.get('/users')
return res.data.map((res: any) => createUserFromResponse(res))
}
export interface UserCreateParams {
name: String
}
export const createUser: (params: UserCreateParams) => Promise<User> = async (params: UserCreateParams) => {
const res = await axios.post('/users', {user: params})
return createUserFromResponse(res.data)
}
最後にコンポーネントを作って終わりです。
frontend/src/App.vue
<template>
<div>
<form>
<label>
name
<input v-model="form.name" type="text"/>
</label>
<button @click.prevent="createUser">保存</button>
</form>
<table>
<thead>
<tr>
<th>id</th>
<td>name</td>
<td>createdAt</td>
<td>updatedAt</td>
</tr>
</thead>
<tbody>
<tr v-for="user in users" :key="user.id">
<th>{{ user.id }}</th>
<td>{{ user.name }}</td>
<td>{{ user.createdAt.format() }}</td>
<td>{{ user.updatedAt.format() }}</td>
</tr>
</tbody>
</table>
</div>
</template>
<script lang="ts">
import {createUser, getUsers} from '/@/apis/users_api'
import {defineComponent} from 'vue'
import User from '/@/models/user'
export default defineComponent({
data() {
return {
form: {
name: '' as string
},
users: [] as User[]
}
},
methods: {
async createUser() {
const user = await createUser(this.form)
this.users.push(user)
this.form.name = ''
}
},
async created() {
this.users = await getUsers()
}
})
</script>
defineComponentにして型定義するだけで、あとはVue2と変わらないですね。
最後に
意外と簡単に型の指定できました。Vue2でもCompositionAPIを入れればできたのかな?
今度はVuexを使った実装もやっていきます!
Discussion