Vueハンズオン

Vueハンズオン用ドキュメント。
インストール〜APIリクエスト、テーブル操作までやってみる。

## インストール

- Node.js

#### Mac

```
$ brew update

# nodebrew インストール
$ brew install nodebrew
$ mkdir -p ~/.nodebrew/src

# node.js インストール
$ nodebrew install-binary stable
## 確認
$ nodebrew ls
v12.16.1
## 適用
$ nodebrew use v12.16.1
## 環境パス適用(zsh用)
$ echo 'export PATH=$HOME/.nodebrew/current/bin:$PATH' >> ~/.zshrc
$ source ~/.zshrc

## node確認
$ node -v
v12.16.1
```

#### Windows

下記手順に沿ってインストール

https://qiita.com/Masayuki-M/items/840a997a824e18f576d8

> Node.jsバージョンの指定はありません

- Vue CLI

```
$ npm install -g @vue/cli

## Version確認
$ vue --version
@vue/cli 4.2.2
```

> 最新版のインストールでOK

> 参考: https://cli.vuejs.org/


## Vueハンズオン

### 1.プロジェクト作成と起動

```
$ vue create my-vue-project

? Please pick a preset: (Use arrow keys)

❯ default (babel, eslint)
  Manually select features

## 確認
$ cd my-vue-project
$ ls -lrt

total 928
-rw-r--r--    1 usami65  staff      73  3 29 14:41 babel.config.js
drwxr-xr-x    4 usami65  staff     128  3 29 14:41 public
drwxr-xr-x    6 usami65  staff     192  3 29 14:41 src
-rw-r--r--    1 usami65  staff     843  3 29 14:41 package.json
drwxr-xr-x  822 usami65  staff   26304  3 29 14:41 node_modules
-rw-r--r--    1 usami65  staff  460067  3 29 14:41 package-lock.json
-rw-r--r--    1 usami65  staff     326  3 29 14:41 README.md

## 起動
$ npm run serve

## 確認
http://localhost:8080/

```

### 2.Bootstrap Vue

- Bootstrap Vueインストール

```
$ vue create my-first-bootstrap-project
$ cd my-first-bootstrap-project
$ npm install bootstrap-vue bootstrap
```

- エントリーポイント(`src/main.js`)へbootstrapを登録

```

import Vue from 'vue'
import App from './App.vue'

import { BootstrapVue, IconsPlugin } from 'bootstrap-vue'
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'

// Install BootstrapVue
Vue.use(BootstrapVue)
// Optionally install the BootstrapVue icon components plugin
Vue.use(IconsPlugin)

Vue.config.productionTip = false

```

- bootstrap使ってみる(Form input)

```
# src/App.vue

<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png">
    <HelloWorld msg="Welcome to Your Vue.js App"/>

    <hr />
    <div>
      <b-form-input v-model="text" placeholder="Enter your name"></b-form-input>
      <div class="mt-2">Value: {{ text }}</div>
    </div>

  </div>
</template>


<script>
import HelloWorld from './components/HelloWorld.vue'

export default {
  name: 'App',
  components: {
    HelloWorld
  },
  data() {
    return {
        text: ''
    }
  }
}
</script>

```

- 確認

```
## 起動
$ npm run serve

## 確認
http://localhost:8080/
```

> 入力フィールドあるはず!!

> 参考: https://bootstrap-vue.js.org/docs/components/form-input

### 3.APIリクエスト

- axiosインストール

```
$ vue create my-first-axios-project
$ cd my-first-axios-project
$ npm install axios
```

- エントリーポイント(`src/main.js`)へbootstrapを登録

```

import Vue from 'vue'
import App from './App.vue'

import { BootstrapVue, IconsPlugin } from 'bootstrap-vue'
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'

// Install BootstrapVue
Vue.use(BootstrapVue)
// Optionally install the BootstrapVue icon components plugin
Vue.use(IconsPlugin)

Vue.config.productionTip = false

```

- APIリクエスト(Get)を書いてみる

> `src/App.vue` の内容を下記へ書き換える


```
<template>
    <div>
        <br>
        <b-button variant="outline-primary" @click="getIp">IPを取得</b-button>
        <hr/>
        <div>Your IP: {{ ipAddress }}</div>
        <br>
        <div>Json Data: {{ jsonData }}</div>
    </div>
</template>

<script>
    import axios from 'axios'

    export default {
        name: 'TestAPP',
        data() {
            return {
                jsonData: '',
                ipAddress: '',
            }
        },
        methods: {
            getIp() {
                const url = 'https://httpbin.org/get';
                axios.get(url)
                    .then((response) => {
                        console.log(response.data);
                        this.jsonData = response.data;
                        this.ipAddress = response.data.origin;
                    })
                    .catch((e) => {
                        alert(e);
                    });
            }
        }
    }
</script>

<style scoped>
</style>
```

- 確認

```
## 起動
$ npm run serve

## 確認
http://localhost:8080/
```

> `IPを取得`ボタンを押すとIPアドレスが表示される。


### 4.テーブル

- ~~作業用プロジェクトを`git clone`する~~

```
$ git clone https://github.com/65usami/vue-hanson-project.git
```

- テーブルデータ コンポーネント作成

> `src/components/HandsOn.vue` の内容を下記へ書き換える

```
<template>
    <div>
        <table border="1">
            <tr>
                <td>{{items[0].age}}</td>
                <td>{{items[0].first_name}}</td>
                <td>{{items[0].last_name}}</td>
            </tr>
            <tr>
                <td>{{items[1].age}}</td>
                <td>{{items[1].first_name}}</td>
                <td>{{items[1].last_name}}</td>
            </tr>
            <tr>
                <td>{{items[2].age}}</td>
                <td>{{items[2].first_name}}</td>
                <td>{{items[2].last_name}}</td>
            </tr>
        </table>
    </div>
</template>

<script>
    export default {
        name: 'HandsOn',
        data() {
            return {
                items: [
                    {age: 40, first_name: 'Dickerson', last_name: 'Macdonald'},
                    {age: 21, first_name: 'Larsen', last_name: 'Shaw'},
                    {age: 89, first_name: 'Geneva', last_name: 'Wilson'},
                ]
            }
        }
    }
</script>

<style scoped>
</style>
```

- 確認

```
## 起動
$ npm run serve

## 確認
http://localhost:8080/
```

### 5.テーブル(Bootstrap)

- ~~作業用プロジェクトを`git clone`する~~

```
$ git clone https://github.com/65usami/vue-hanson-project.git
```

- テーブルデータ コンポーネント作成

> `src/components/HandsOn.vue` の内容を下記へ書き換える

```
<template>
    <div>
        <b-table striped hover :items="items"></b-table>
    </div>
</template>

<script>
    export default {
        data() {
            return {
                items: [
                    { age: 40, first_name: 'Dickerson', last_name: 'Macdonald' },
                    { age: 21, first_name: 'Larsen', last_name: 'Shaw' },
                    { age: 89, first_name: 'Geneva', last_name: 'Wilson' },
                ]
            }
        }
    }
</script>
```

- 確認

```
## 起動
$ npm run serve

## 確認
http://localhost:8080/
```

- 参考

[bootstrap-vue](https://bootstrap-vue.js.org/docs/components/table)


### 6.複数コンポーネント

- ~~作業用プロジェクトを`git clone`する~~

```
$ git clone https://github.com/65usami/vue-hanson-project.git
```

- コンポーネント作成

> `src/components/HandsOn1.vue`


```
<template>
    <div>
        Hands on 1
    </div>
</template>

<script>
    export default {
        name: 'HandsOn1',
    }
</script>

<style scoped>
</style>
```

> `src/components/HandsOn2.vue`

```
<template>
    <div>
        Hands on 2
    </div>
</template>

<script>
    export default {
        name: 'HandsOn1',
    }
</script>

<style scoped>
</style>
```

- 複数コンポーネント参照

> `src/App.vue`

```
<template>
  <div id="app">
    <HandsOn1></HandsOn1>
    <br>
    <HandsOn2></HandsOn2>
  </div>
</template>

<script>
import HandsOn1 from "./components/HandsOn1";
import HandsOn2 from "./components/HandsOn2";

export default {
  name: 'App',
  components: {
    HandsOn1,
    HandsOn2
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
</style>
```

- 確認

```
## 起動
$ npm run serve

## 確認
http://localhost:8080/
```

### 7.Props(親コンポーネントから子コンポーネントへの値渡し)

- ~~作業用プロジェクトを`git clone`する~~

```
$ git clone https://github.com/65usami/vue-hanson-project.git
```

- 子コンポーネント

> `src/components/HandsOn3.vue`

```
<template>
    <div>
        Hands on 3
        <hr>
        {{msg}}
    </div>
</template>

<script>
    export default {
        name: 'HandsOn2',
        props: {
            msg: { type: String, default: '' },
        },
    }
</script>

<style scoped>
</style>
```

- 親コンポーネント

> `src/App.vue`

```
<template>
  <div id="app">
    <HandsOn1></HandsOn1>
    <br>
    <HandsOn2></HandsOn2>
    <br>
    <HandsOn3 msg="テストメッセージ"></HandsOn3>
  </div>
</template>

<script>
import HandsOn1 from "./components/HandsOn1";
import HandsOn2 from "./components/HandsOn2";
import HandsOn3 from "./components/HandsOn3";

export default {
  name: 'App',
  components: {
    HandsOn1,
    HandsOn2,
    HandsOn3,
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
</style>
```

- 確認

```
## 起動
$ npm run serve

## 確認
http://localhost:8080/
```

### 8.ルーティング

コンポーネントのルーティング。URLの動的変更。

- ~~作業用プロジェクトを`git clone`する~~

```
$ git clone https://github.com/65usami/vue-hanson-project.git
```

- vue-routerインストール

```
$ cd vue-hanson-project
$ npm install vue-router
```

- ルーティング用 コンポーネント1

> `src/HandsOn.vue`

```
<template>
    <div>
        HandsOn
    </div>
</template>

<script>
    export default {
        name: "HandsOn"
    }
</script>

<style scoped>
</style>
```

- ルーティング用 コンポーネント2

> `src/HandsOn2.vue`

```
<template>
    <div>
        HandsOn2
    </div>
</template>

<script>
    export default {
        name: "HandsOn2"
    }
</script>

<style scoped>
</style>

```
- ルーティング用 コンポーネント3

> `src/HandsOn2.vue`

```
<template>
    <div>
        HandsOn3
    </div>
</template>

<script>
    export default {
        name: "HandsOn3"
    }
</script>

<style scoped>
</style>
```

- vue-router 設定

> `src/router/index.js`

```
import Vue from 'vue'
import Router from 'vue-router'
import HandsOn from "../components/HandsOn";
import HandsOn2 from "../components/HandsOn2";
import HandsOn3 from "../components/HandsOn3";

Vue.use(Router)

export default new Router({
    mode: 'history',
    routes: [
        {
            path: '/',
            name: 'HandsOn',
            component: HandsOn
        },
        {
            path: '/2',
            name: 'HandsOn2',
            component: HandsOn2
        },
        {
            path: '/3',
            name: 'HandsOn3',
            component: HandsOn3
        },
    ]
})
```

- `main.js`への反映

> `src/main.js`

```
import Vue from 'vue'
import App from './App.vue'
import router from './router'

import { BootstrapVue, IconsPlugin } from 'bootstrap-vue'
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'

// Install BootstrapVue
Vue.use(BootstrapVue)
// Optionally install the BootstrapVue icon components plugin
Vue.use(IconsPlugin)

Vue.config.productionTip = false

new Vue({
  router,
  render: h => h(App),
}).$mount('#app')
```

- ルーティングコントロール コンポーネント

> `src/App.vue`

```
<template>
  <div id="app">
    <div class="link-container">
      <router-link class="link" :to="{ name: 'HandsOn' }">HansOn</router-link>
      <router-link class="link" :to="{ name: 'HandsOn2' }">HansOn2</router-link>
      <router-link class="link" :to="{ name: 'HandsOn3' }">HansOn3</router-link>
    </div>
    <hr>
    <router-view/>
  </div>
</template>

<script>

export default {
  name: 'App'
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

.link-container {
  display: flex;
  flex-direction: row;
}

.link {
  margin-left: 20px;
}
</style>
```

- 確認

```
## 起動
$ npm run serve

## 確認
http://localhost:8080/
```

### 9. Vueライフサイクルフック

ロード時に処理してみよう

> [ライフサイクル全体像](https://swallow-incubate.com/archives/blog/20190422/?doing_wp_cron=1588755596.5943980216979980468750)

- ~~作業用プロジェクトを`git clone`する~~

```
$ git clone https://github.com/65usami/vue-hanson-project.git
```

- ロード時にフックするコンポーネント

> `src/components/HandsOn.vue`

```
<template>
    <div>
        <div v-if="ipAddress">
            <div>Your IP: {{ ipAddress }}</div>
            <hr>
            <div>Json Data: {{ jsonData }}</div>
        </div>
        <div v-else>
            <b-spinner variant="primary"></b-spinner>
        </div>
    </div>
</template>

<script>
    import axios from 'axios'

    export default {
        name: "HandsOn",
        data() {
            return {
                jsonData: '',
                ipAddress: '',
            }
        },
        created() {
            // spinner確認のために意図的に1秒遅延
            setTimeout(() => this.setIp(), 1000);
        },
        methods: {
            setIp() {
                const url = 'https://httpbin.org/get';
                axios.get(url)
                    .then((response) => {
                        console.log(response.data);
                        this.jsonData = response.data;
                        this.ipAddress = response.data.origin;
                    })
                    .catch((e) => {
                        alert(e);
                    });
            }
        },
    }
</script>

<style scoped>

</style>
```

> `src/App.vue` の内容を下記へ書き換える

```
<template>
  <div id="app">
    <HandsOn></HandsOn>
  </div>
</template>

<script>
  import HandsOn from "./components/HandsOn";

export default {
  name: 'App',
  components: {HandsOn},
  comments: {
    HandsOn
  }

}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
</style>
```


- 確認

```
## 起動
$ npm run serve

## 確認
http://localhost:8080/
```

### 10. グラフ描画


### 11. Vuex

> 結構難しい!!

データの状態管理一元化を行う

![vuex-6-638.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/124012/9121a740-1df4-a144-9095-8d64ffdec6a0.jpeg)

使用例:
- 認証用Tokenの保持や再利用
- 複数コンポーネント間でのデータ管理
- 永続的なデータ保持(ブラウザが有効な限り)

参考:
https://qiita.com/fruitriin/items/42b0ebc5f8a524a0ae17


### 12. ルーティング(ナビゲーションガード)

ページ(コンポーネント)のアクセス可否コントロール
> Token認証等で利用

### 13. VueをDockerで公開する

docker-composeを利用してVueをDockerで公開しよう

> 前提:
> dockerインストール済み
> docker-composeインストール済み

- ~~作業用プロジェクトを`git clone`する~~

```
$ git clone https://github.com/65usami/vue-hanson-project.git
```

- Dockerfileの作成

> `vue-hanson-project/Dockerfile` を作成

```
FROM node:lts-alpine as build-stage

WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

FROM nginx:stable-alpine as production-stage
COPY --from=build-stage /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;","-c","/etc/nginx/nginx.conf"]
```

- docker-compose.ymlの作成

> `vue-hanson-project/docker-compose.yml` を作成

```
version: '3.7'

services:
  web:
    build: .
    ports:
      - '80:80'
```

- 確認

```
## 起動
$ docker-compose up

## 確認
http://localhost
```