Vue CLIで新規プロジェクト作成と、よくある雛形の実装

# Vue CLIで新規プロジェクト作成と、よくある雛形の実装

この記事は[サーバーレスWebアプリ Mosaic](https://mosaic.w2or3w.com "Mosaic")を開発して得た知見を振り返り定着させるための[ハンズオン記事](https://qiita.com/w2or3w/items/87b57dfdbcf218de91e2)の1つです。

以下を見てからこの記事をみるといい感じです。

* [サーバーレスWebアプリを開発するためのCloud9環境構築](https://qiita.com/w2or3w/items/6cda6177ca6b0f704974)

## はじめに
Vue CLI 3でSPA(シングルページアプリケーション)の雛形を新規作成します。
UI用のフレームワークとして、マテリアルデザインのVuetifyを利用。
ヘッダー、フッター、いくつかのページへのルーティング、ハンバーガーメニューなどを実装します。

## コンテンツ

### GithubにWebアプリプロジェクト用のリポジトリを作成
リポジトリ名は `sample_vue_project` とします。
Vueのプロジェクト名と同じにしますので、大文字は使えません。

### Cloud9にリポジトリをクローン
```
$ git clone https://github.com/{YourGithubID}/sample_vue_project.git
```

### Vueプロジェクトの作成

以下のコマンドで新規プロジェクトを作成できます。

```
$ vue create sample_vue_project
```
プロジェクト作成が終わるとこんな感じになってると思います。
![Screenshot 2019-12-22 at 23.02.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/394775/fbc1f191-011f-db14-acb8-3249ba814d65.png)

- Vueプロジェクト実行  
Cloud9でVueのプロジェクトを実行するために、プロジェクトディレクトリ直下に以下のファイルが必要です。  

```vue.config.js
module.exports = {
  "devServer": {
    host: '0.0.0.0',
    public: '0.0.0.0:8080',
    disableHostCheck: true
  }
}
```
~~(※理由は良く知りません、、。誰か教えて下さい。)~~
@NonbiriTarsan さんが情報を提供してくださいました。(2020.1.27)
---ここから---
[こちらのサイト](https://fullstackengineerfrombeginner.com/vue-cli-in-cloud9-resolve-invalidhostheader/)によりますと、上に書いた設定`disableHostCheck:true`だと、根本的な解決ではないそうで、以下のような設定とするべきだそうです。

```vue.config.js
module.exports = {
    devServer: {
        public: "${$C9_PID}.vfs.cloud9.${MY_REGION}.amazonaws.com"
    }
}
```

`${MY_REGION}`はいいと思いますが、`${$C9_PID}`が何か少し迷いました。
Cloud9の環境詳細にある、Environment ARN
arn:aws:cloud9:ap-northeast-1:123456789012:environment:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
これの、xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxの部分がソレです。

なるほどなるほど、、、って、
前の設定と後の設定で、何がどう変わるのか、正直理解しきれていないのですが、そういう事みたいです。
---ここまで---

プロジェクトディレクトリで以下のコマンドにより、プロジェクトのビルドの後にサーバーが起動して、Webアプリにアクセスできるようになります。

```
$ npm run serve
```

ビルドが終わりサーバーが起動したら、上部メニューバーにある `Preview > Preview Running Application` からアクセスしてください。
![Screenshot 2019-12-22 at 23.58.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/394775/c6386d50-86a0-299f-0302-6b2a855e73a8.png)
こんな感じのVueのWelcome画面が表示されればOKです。
右上の方にある `Pop Out Into New Window` によりブラウザの別タブで開くことができ、またそうすることでデベロッパーツールも利用できるようになります。
![Screenshot 2019-12-23 at 00.00.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/394775/f2b3577f-fd6c-9c66-026e-f07c370411d6.png)

起動したサーバーは、Ctrl+Cで停止できます。

### Vuetifyの利用
プロジェクトディレクトリで以下のコマンドにより、Vuetifyをプロジェクトにインストールすることができます。

```
$ vue add vuetify
? Choose a preset: Default (recommended)
```
インストールできたら実行してみてください。デザインがVuetifyのものに変わっています。
![Screenshot 2019-12-25 at 21.07.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/394775/cd8ac601-d545-63ea-65eb-d915b407025a.png)

### いくつかのページとページ遷移のためのルーティング
ページ遷移のために、ルーティング制御プラグインVue Routerを利用します。
まずはインストールします。

```
$ npm install vue-router
```
必要な画面分、ViewとComponentのvueファイルを作成します。
ここではHomeとAboutの2画面作成し、また、お互いの画面を行き来できるようにしておきます。

以下のファイルを新規作成します。
src/views/Home.vue
src/views/About.vue
src/components/Home.vue
src/components/About.vue
src/router.js

最初からある以下のファイルは削除します。
src/components/HelloWorld.vue

* ソースコードの更新

```src/view/Home.vue
<template> 
  <Home /> 
</template> 
 
<script> 
  import Home from '../components/Home'  
  export default { 
    components: { 
      Home 
    } 
  } 
</script> 
```
```src/view/About.vue
<template> 
  <About /> 
</template> 
 
<script> 
  import About from '../components/About' 
  export default { 
    components: { 
      About 
    } 
  } 
</script> 
```
```src/components/Home.vue
<template>
  <v-container>
    <p>ホーム</p>
    <router-link to="about" >link to About</router-link>
  </v-container>
</template>
```
```src/components/About.vue
<template>
  <v-container>
    <p>アバウト</p>
    <router-link to="/" >link to Home</router-link>
  </v-container>
</template>
```
```src/router.js
import Vue from 'vue'; 
import Router from 'vue-router'; 
import Home from './views/Home.vue'; 
import About from './views/About.vue'; 
 
Vue.use(Router); 
 
export default new Router({ 
  routes: [ 
    { 
      path: '/', 
      name: 'home', 
      component: Home,
    }, 
    { 
      path: '/about', 
      name: 'about', 
      component: About, 
    }, 
  ] 
});
```
```src/main.js
import Vue from 'vue'
import App from './App.vue'
import vuetify from './plugins/vuetify';
import router from './router';

Vue.config.productionTip = false

new Vue({
  vuetify,
  router, 
  render: h => h(App)
}).$mount('#app')
```
```src/App.vue
<template>
  <v-app>
    <v-content>
      <router-view></router-view>
    </v-content>
  </v-app>
</template>

<script>
export default {
  name: 'App',
  components: {
  },
  data: () => ({
    //
  }),
};
</script>
```
(※JavaScript、Vue.js、Vuetifyの基本構文についてはここでは触れません。必要な場合はどこかで学んできてください。)

実行してみましょう。
お互いのページを行き来するだけのシンプルなSPAが実行できました。
![Screenshot 2019-12-29 at 07.53.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/394775/5e42b3c2-cce2-11e4-c512-d71c1ce60234.png)

### ヘッダーとフッター

見た目が寂しいのでヘッダーとフッターを付けましょう。

```src/App.vue
<template>
  <v-app>
    <v-app-bar app>
      <v-toolbar-title>
        Sample Vue Project
      </v-toolbar-title> 
    </v-app-bar>
    
    <v-content>
      <router-view></router-view>
    </v-content>

    <v-footer app>
      <v-card-title>
        <v-btn fab small class="mx-2" color="white" href="https://twitter.com/w2or3w" target="_blank">
          <v-icon>mdi-twitter</v-icon>
        </v-btn>
        <v-btn fab small class="mx-2" color="white" href="https://www.facebook.com/w2or3w" target="_blank">
          <v-icon>mdi-facebook</v-icon>
        </v-btn>
        <v-btn fab small class="mx-2" color="white" href="https://www.instagram.com/w2or3w" target="_blank">
          <v-icon>mdi-instagram</v-icon>
        </v-btn>
      </v-card-title>
      <v-card-text>
        <p>Copyright © Sample Vue Project</p>
      </v-card-text>
    </v-footer>
  </v-app>
</template>

<script>
export default {
  name: 'App',
  components: {
  },
  data: () => ({
    //
  }),
};
</script>
```
ヘッダー(`v-app-bar`)とフッター(`v-footer`)を加えました。

実行してみましょう。
ヘッダーとフッターが付いてそれっぽくなりましたね。
(※SNSリンクはワタシのものなので自分のものに変えてくださいね。(フォロー歓迎です。))
![Screenshot 2019-12-29 at 09.56.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/394775/9a0d94ba-59ce-75ca-1976-29565f194e54.png)

### ハンバーガーメニュー
これも最近のサイトには良くありがちなハンバーガーメニューを付けてきましょう。

```src/App.vue
<template>
  <v-app>
    <v-navigation-drawer app v-model="drawer">
      <v-list nav dense>
        <v-list-item-group>
          <v-list-item>
            <v-list-item-title @click="onHome">HOME</v-list-item-title>
          </v-list-item>
          <v-list-item>
            <v-list-item-title @click="onAbout">ABOUT</v-list-item-title>
          </v-list-item>
        </v-list-item-group>
      </v-list>
    </v-navigation-drawer>

    <v-app-bar app>
      <v-app-bar-nav-icon @click="drawer = !drawer"></v-app-bar-nav-icon>
      <v-toolbar-title>
        Sample Vue Project
      </v-toolbar-title> 
    </v-app-bar>

    <v-content>
      <router-view></router-view>
    </v-content>

    <v-footer app>
      <v-card-title>
        <v-btn fab small class="mx-2" color="white" href="https://twitter.com/w2or3w" target="_blank">
          <v-icon>mdi-twitter</v-icon>
        </v-btn>
        <v-btn fab small class="mx-2" color="white" href="https://www.facebook.com/w2or3w" target="_blank">
          <v-icon>mdi-facebook</v-icon>
        </v-btn>
        <v-btn fab small class="mx-2" color="white" href="https://www.instagram.com/w2or3w" target="_blank">
          <v-icon>mdi-instagram</v-icon>
        </v-btn>
      </v-card-title>
      <v-card-text>
        <p>Copyright © Sample Vue Project</p>
      </v-card-text>
    </v-footer>
    
  </v-app>
</template>

<script>
export default {
  name: 'App',
  components: {
  },
  data: () => ({
    drawer: false,
  }),
  methods:{
    onHome() {
      if(this.$route.path != '/'){
        this.$router.push({ path: '/' });
      }
    }, 
    
    onAbout() {
      if(this.$route.path != '/about'){
        this.$router.push({ path: 'about' });
      }
    }, 
  }
};
</script>
```
メニュー本体(`v-navigation-drawer`)と、`v-app-bar`にアイコン(`v-app-bar-nav-icon`)を加えました。また、dataにdrawerフラグも追加しています。

実行してみましょう。
ハンバーガーメニューが付きましたね。
![Screenshot 2019-12-29 at 09.56.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/394775/9ec53849-779b-e963-5170-63ba809d3197.png)![Screenshot 2019-12-29 at 09.57.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/394775/5a3c915f-1f83-6c1b-8d05-c3102bf171be.png)


## あとがき
ここで作成したプロジェクトはGitHubの以下に置いておきますね。
https://github.com/ww2or3ww/sample_vue_project/tree/work1

UIデザイン(見た目)ってすごく大事だと思うのですが、実装するにあたってすごく面倒だし消耗させられる部分でもあります。
[CSS完全に理解したTシャツ](https://twitter.com/ikeji/status/1174570034196140033)がバズってましたが、そういう事ですよね。
![EEzpN-JU0AA7u4z.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/394775/32f32778-eb41-5a87-b2eb-af46a508cd1c.jpeg)

VuetifyやBulmaのようなUIフレームワークを利用することで、UIデザインにあまり消耗せず、デフォルトでそれっぽく仕上げることができますのでとても助かります。
ワタシはGoogleが好きなので、Googleが推しているMaterial Designを継承しているVuetifyを選びました。

このハンズオン記事では、極力CSSは書かない方針でいきたいと思いますのでよろしくお願いします。

とはいえ、思い入れをもってアプリを作ろうとしたときにデザインは避けて通れません。UIフレームワークを利用した上で、こだわりという名の微調整をCSSでチマチマとする羽目になるでしょう。

最後に、デザインのために役立つサイトを置いておきますね。
配色に悩んだらら[ここ](https://www.materialpalette.com)を参照すべし。
アイコンは[ここ](https://iconify.design/icon-sets/mdi/)で探しています。
[マテリアルデザインのホーム](https://material.io/)は、見てるだけで楽しいです。