#内容
この記事は、Vueを勉強しているけどTypeScriptベースでアプリを構築したい!という方へ向けて実際に一から環境構築していく内容となっております!
ついでにDockerで構築を行いコンテナ環境下で動くアプリを作成していこうと思います!
構成としては Docker + Vue.js + Express(Node.js) + MySQLをTypeScript ベースで構築をしつつモジュール拡張性を持たせる環境を目指していこうと思います!
※初めての記事となりますので至らぬ点があるかと思いますが、優しくご指摘を頂けますと幸いです。
目指す状態
- アプリケーションコンテナ
- Vue + Vuex + Vuetify + TypeScript
- APIサーバーコンテナ
- Express + Sequelize + TypeScript
- データベースコンテナ
- MySQL
「Vue → Vuex → HTTP通信(Axios) → API(Express) → MySQLデータ取得(Sequelize) → Storeにレスポンス(Express) →Vue」
といった流れになります。
対象者
- Docker,Docker-Compose がインストール済みである(利用可能な状態)
- JavaScript / Vue.jsの基礎を理解している
Docker上でさくっと環境構築することを目的にしておりDockerについて詳しく触れる内容ではございません。
Dockerってそもそも何?という方は下記の記事をご参考にしていただくことをオススメします。
【図解】Dockerの全体像を理解する -前編-
作業手順
Vue 編
- アプリケーションコンテナ作成
- コンテナ上でVueアプリを構築
- コンテナ起動時にアプリ実行を自動化
- Vueの主要ライブラリを導入
- Vuetify 導入
- テスト用のカウンターアプリ作成
MySQL 編
- DBコンテナの作成
- テストデータの投入
- MySQLへのアクセス
Dockerで【TypeScript+Vue+Express+MySQL】の環境を構築する方法~MySQL編~
Express 編
- APIコンテナ作成
- ts-node/nodemonの導入
- Expressサーバー構築
- ルーティング設定
- APIハンドラーの設定
- APIのテンプレートを作成
Dockerで【TypeScript+Vue+Express+MySQL】の環境を構築する方法~Express編~
Sequelize 編
- コンテナ同士のネットワークを設定
- Sequelize 導入
- Model作成
- GetTests APIを完成させる
- フロント側からAxiosでAPIをコールする
- レスポンスをVuexにバインドして描写する
Dockerで【TypeScript+Vue+Express+MySQL】の環境を構築する方法~Sequelize編~
##1. アプリケーションコンテナの作成
まずはdocker-composeでコンテナを作成するためのファイルを作成していきます。
下記の構成で必要なファイルを作成します。
root
├─ docker
│ └─ app
│ └─ Dockerfile
└─ docker-compose.yml
docker-compose.yml
version: "3"
services:
app:
container_name: app_container
build: ./docker/app
ports:
- 8080:8080
volumes:
- ./app:/app
stdin_open: true
tty: true
environment:
TZ: Asia/Tokyo
こちらが根幹になる docker-compose の設定ファイルです。
後からAPIサーバーコンテナ、DBコンテナを追加しますが一旦APPコンテナのみ作成していきます。
Dockerfile
FROM node:12.13-alpine
RUN yarn global add @vue/cli
WORKDIR /app
今回はベースイメージとして軽量なalpineを採用します。
コンテナ内でvue-cliを使うのでグローバルインストールを行っておきます。
コンテナ・ビルド
$ docker-compose build
先ほど作成した設計書を下にイメージをビルド
コンテナ・起動
$ docker-compose up -d
ビルドされたコンテナを実際に起動します。
-d をつけることでデタッチドモード(バックグランド)で起動します。
起動中のコンテナを確認
$ docker ps
下記が出力結果となりますので実際に起動されているかチェックしましょう。
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
// ここに作成したコンテナが表示されていればOK
##2. コンテナ上でVueアプリ立ち上げ
ここまででベースのコンテナができたので、実際にコンテナの中に入ってアプリを構築していきます。
コンテナにアクセス
$ docker exec -it app_container sh
/app # // こうなってればコンテナにアクセスできています
Vueアプリ作成
$ vue create app
マニュアル選択
TypeScript あり
Router あり
Vuex あり
Babel なし
バージョンは2.x。
Use class-style component syntax? Yes ※これ大事
yarn ※パッケージはYarnでいこうと思います。
あとはお好みで。
起動
$ cd app
$ yarn serve
localhost:8080 にアクセスして表示されればOK!
ここまででコンテナの中にアプリを作成し、コンテナの内部からアプリを実行するところまでが完了です。
毎回この作業は面倒なのでコンテナ起動時に中のアプリケーションも自動で実行される設定を行っていきます。
一旦アプリを停止
$ controll + c
コンテナから抜ける
$ exit
コンテナの停止
$ docker-compose stop
##3. コンテナ起動時にアプリ実行を自動化
ディレクトリ構造を変更
root直下のappディレクトリはお邪魔なのでどこかに行っていただきます。
Before
root
├─ app // ここに移植(こいつは削除)
│ └─ app // このディレクトリごと
│ ├─ node_modules
│ ├─ public
│ ├─ src
│ その他もろもろ
│
├─ docker
│ └─ app
│ └─ Dockerfile
└─ docker-compose.yml
After
root
├─ app
│ ├─ node_modules
│ ├─ public
│ ├─ src
│ その他もろもろ
│
├─ docker
│ └─ app
│ └─ Dockerfile
└─ docker-compose.yml
Composeの設定ファイルに起動時のコマンドを追加
docker-compose.yml
version: "3"
services:
app:
container_name: app_container
build: ./docker/app
ports:
- 8080:8080
volumes:
- ./app:/app
stdin_open: true
tty: true
environment:
TZ: Asia/Tokyo
command: yarn serve // こいつを追加
コンテナ起動
$ docker-compose up -d
この状態でlocalhost:8080にアクセスしてアプリが立ち上がっていれば成功です!
yarn serveをしなくてもdockerコンテナが起動時にアプリも起動されるようになります!
prettier の導入(お好み)
JavaScriptのセミコロンが嫌いなので自動整形してくれるPrettierを導入します。
このあたりはお好みでどうぞ。
コンテナへアクセス
$ docker exec -it app_container sh
Prettierのインストール
$ yarn add prettier --dev
app/.prettierrc 作成
{
"singleQuote": true,
"semi": false
}
##4. 主要なVueライブラリの導入
実はこちらが今回の本命。
VueをTypeScriptで記述しつつ、デコレータ(@)を使うことですっきりと直感的に記述が可能になるライブラリを導入します。
この先Vueファイルで実際に見慣れない書き方が出てきますが、これらのライブラリがうまいことすっきりさせてくれています。
- vue-class-component
- vue-property-decorator
- vuex-class
- vuex-module-decorators
$ yarn add vue-class-component vue-property-decorator vuex-class vuex-module-decorators
下記の記事がとてもわかりやすいのでご興味ある方は是非お読みください。
App.vueを編集
src/App.vue
<template>
<div id="app">
<router-view />
</div>
</template>
<style>
#app {
width: 95%;
margin: 0 auto;
}
</style>
表示するコンポーネントはRouter側に任せたいので router-link を削除し router-view に差し替えます。
不要なファイルを削除
├─ assets
│ └─ logo.ping // 削除
├─ components
│ └─ HelloWorld.vue // 削除
├─ views // ディレクトリごと削除
│ ├─ Home
│ └─ About
今後使わないファイルさんたちにはご退場いただきましょう。
Test.vue 作成
/pages/Test.vue
<template>
<div class="test">
<h1>Test Page</h1>
</div>
</template>
<script lang="ts">
import { Vue, Component } from 'vue-property-decorator'
@Component
export default class Test extends Vue {}
</script>
下記をチェックするためにテスト用のコンポーネントを作成します。
- 正しくルーティングができるか
- コンポーネントが正しく読み込めるか
Routerを編集
/router/index.ts
import Vue from 'vue'
import VueRouter, { RouteConfig } from 'vue-router'
import Test from '../pages/Test.vue'
Vue.use(VueRouter)
const routes: Array<RouteConfig> = [
{
path: '/',
name: 'Test',
component: Test,
},
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes,
})
export default router
作成したTestコンポーネントがルートで呼ばれるようにルーティングの設定を行います。
ページ確認
リロードして「Test Page」が表示されればOK!
この要領でURLとコンポーネントをつなげていけば自由にページを増やすことができます!
##5. Vuetify 導入
ライブラリのインストール
yarn add vuetify
yarn add --dev sass sass-loader fibers deepmerge
vuetifyだけだとエラーが出るので必要なライブラリ群も合わせてインストールします。
プラグインファイルの作成
src/plugins/vuetify.ts
import Vue from 'vue'
import Vuetify from 'vuetify'
import 'vuetify/dist/vuetify.min.css'
Vue.use(Vuetify)
const opts = {}
export default new Vuetify(opts)
こちらはアプリ全体でVuetifyを適用するための設定ファイルです。
main.ts編集
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import vuetify from './plugins/vuetify' // 追加
import 'vuetify/dist/vuetify.min.css' // 追加
Vue.config.productionTip = false
new Vue({
vuetify, // 追加
router,
store,
render: (h) => h(App),
}).$mount('#app')
上記で作成したプラグインを読み込むことでアプリケーション内でVuetifyが使えるようになります。
App.vue 編集
<template>
<v-app> <!-- 追加 -->
<div id="app">
<router-view />
</div>
</v-app> <!-- 追加 -->
</template>
最後にアプリ本体のViewを囲ってあげればOKです。
Test.vue編集
<v-btn large color="primary">テスト</v-btn>
これでボタンが表示されていればVuetifyの導入が完了です!お疲れ様でした!
あとはこの要領でページを自由に増やしていけばOKです!
##6. テスト用のカウンターアプリ作成
ここまででアプリケーションの雛形は完成したのでVuex/Storeが正しく使えるかチェックしがてらカウンター機能を作っていきます。
Counter.vue 作成
pages/Counter.vue
<template>
<div class="counter">
<h1>Counter</h1>
</div>
</template>
<script lang="ts">
import { Vue, Component } from 'vue-property-decorator'
@Component
export default class Counter extends Vue {}
</script>
Counterページの作成
- router/index.ts 編集
import Vue from 'vue'
import VueRouter, { RouteConfig } from 'vue-router'
import Test from '../pages/Test.vue'
import Counter from '../pages/Counter.vue' //追加
Vue.use(VueRouter)
const routes: Array<RouteConfig> = [
{
path: '/',
name: 'Test',
component: Test,
},
{
path: '/counter', // 追加
name: 'Counter', // 追加
component: Counter, // 追加
},
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes,
})
export default router
「localhost:8080/counter」 で上記のコンポーネントがレンダリングされるようにルーティングを設定します。
Storeモジュールの作成
store/modules/**counter.ts**
import {
getModule,
Module,
VuexModule,
Mutation,
Action,
} from 'vuex-module-decorators'
import store from '../index'
type CounterState = {
count: number
}
@Module({ store, dynamic: true, namespaced: true, name: 'Test' })
class CounterModule extends VuexModule implements CounterState {
count = 0
@Mutation
SET_COUNT(payload: number) {
this.count = payload
}
@Action
async increment() {
this.SET_COUNT(this.count + 1)
}
@Action
async decrement() {
this.SET_COUNT(this.count - 1)
}
@Action
async reset() {
this.SET_COUNT(0)
}
}
export const counterModule = getModule(CounterModule)
はじめに導入した「vuex-module-decorators」で直感的にStoreモジュールを記述することができます。
今回は "count" というデータを用いて Actionが発火された時に count が 動的に変化するかを確認していきます。
tsconfig.json 編集
tsconfig.json
"experimentalDecorators": true, // 追加
Linterに怒られる方はこちらを設定してください。
Counter.vue 編集
pages/Counter.vue
<template>
<v-card>
<v-container>
<v-card-title>Counter</v-card-title>
<v-divider />
<v-container>
<v-avatar v-for="item in count" :key="item" color="primary">
<span class="white--text headline">{{ item }}</span>
</v-avatar>
</v-container>
<v-divider />
<v-card-actions>
<v-btn @click="increment">+</v-btn> <!-- クリックされた時にincrementメソッドを実行 -->
<v-btn @click="decrement">-</v-btn> <!-- クリックされた時にdecrementメソッドを実行 -->
<v-btn text @click="reset">Reset</v-btn> <!-- クリックされた時にresetメソッドを実行 -->
</v-card-actions>
</v-container>
</v-card>
</template>
<script lang="ts">
import { Vue, Component } from 'vue-property-decorator'
import { counterModule } from '../store/modules/counter'
@Component
export default class Counter extends Vue {
get count() {
return counterModule.count // Store の count をそのまま返す
}
/**
* カウントをプラスする
*/
increment() {
counterModule.increment() // Store の increment メソッドを発火
}
/**
* カウントをマイナスする
*/
decrement() {
counterModule.decrement() // Store の decrement メソッドを発火
}
/**
* カウントをリセットする
*/
reset() {
counterModule.reset() // Store の reset メソッドを発火
}
}
</script>
<style lang="scss" scoped></style>
ここは存分にVuetifyの恩恵にあやかるとしましょう。
先ほどの count の数だけ v-avatar が生成されるように編集したらあとは動かすのみ!
動作確認!
[+] [-] を押してカウンターが増減すればOKです!
お疲れ様でした!
簡単なカウンターを作成してVuex(Store)の挙動を確認してみましたが、StoreのAction内でAPI通信を入れていくことで実際のDBとの連携が可能になります。次回はAPIサーバーと実際に操作するDBのコンテナ作成を行っていこうと思います!
次回
Dockerで【TypeScript+Vue+Express+MySQL】の環境を構築する方法~MySQL編~
参考
Dockerで【TypeScript+Vue+Express+MySQL】の環境を構築する方法~Express編~
Dockerで【TypeScript+Vue+Express+MySQL】の環境を構築する方法~Sequelize編~