Vue Web App Tutorial

# 11月16日 Web Application 製作入門
[富山IT勉強会#4](https://toyama-it.connpass.com/event/151572/) のハンズオン資料をまとめたものです。
非エンジニア、およびWebアプリ未経験者を対象に簡単なWebアプリ(SPA)を作成するハンズオンを行います。

## このハンズオンのゴール(目的)

- **必要最低限の知識で** Webアプリを作れるようになる(概念、環境構築 ～ Local環境での動作確認)
- 今後、更にレベルアップしていく際の切り口を共有する

## おことわり
一日(4h ほど)で行う予定であるため、最低限知っていれば取りあえずアプリが作れる程度に厳選しております。
そのため、かなり内容は端折っておりますのでご了承ください。
また、私自身プログラミング経験はWindowsアプリケーションがほとんどで、
Web関係は直近一年弱程度ですので情報にはかなり偏りがあります。
また、誤情報等、お気付きの場合にはご指摘頂けると幸いです。

## やること
- JavaScript入門(環境構築〜変数、制御構文、クラス、関数を最小限、簡単に。npmコマンドにもふれる)
- Node.js、Vue を使用したWebアプリケーションを作成(Vue基本構文, Vue/Cliコマンド)
- expressを使用したWebAPIの作成とWebアプリとの通信(express 最小構成でのWebAPIサーバー構築)
- 時間があった時のオプション
    - PythonでもWebAPIを作成してみる(Flask 最小構成)
        - -> AI 機能を組み込んだWebAPIへ応用可?
    - Electronを使用したデスクトップアプリ化(electron-vueを使用)
        - -> Web技術を使用したデスクトップアプリ開発へ繋げたい場合
    - CSSフレームワークを使用してみる(vuetify)
        - -> もっと見た目をきれいに、機能的にしたい場合

## やらないこと
- formタグを使ったGet、Post等の通信
- yarn
- 本番環境へのデプロイ(ローカル環境でのみの作業とします)
- セキュリティ関連
- DataBase操作

## 1. Web(Server - Client 型アプリケーション) の仕組み

本当にザックリですが図を作ってみました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117696/d4333163-bfd5-1369-a0a1-133aeab40ad0.png)

Webサイト、Webアプリなどは、このClient-Server型のアプリケーションになります。
Client Side はChrome等のブラウザを通してサーバーへリクエストを行います。
サーバーPCの中では、この時Apache等のサーバーアプリやデータベースが起動していて、
リクエストに応じた処理を行い、結果をHtmlファイルやJson等で返信します(レスポンス)。

今回は、この図でいうバックエンドのサーバーアプリ部分をexpressで作ります。
また、レスポンスとして返すHtmlファイルを、Vueを使って作成します。

## 2. 環境構築
### Node.jsのインストール

nodejs.org :[https://nodejs.org/ja/](https://nodejs.org/ja/)からLTS版をダウンロード。
ダウンロードされたインストーラーを実行してインストールを行ってください。

<img width="1142" alt="Node_js.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117696/cfdd78f6-b351-6c3e-e33a-34bf0719c3b4.png">

インストール完了後、以下のコマンドをshellへ入力して、バージョンが返ってきたらOK。

```bat
node -v
npm -v
```

### visual studio code (vscode) のインストール
vscodeを用いてハンズオンを行いますが、お気に入りのエディタがある場合はそちらを使っていただいてもかまいません。

[https://code.visualstudio.com/download](https://code.visualstudio.com/download) から自分のOSに合わせたインストーラーをダウンロードし、インストールを行ってください。

## 3. JavaScript(というかNode.js)入門

### まずはhello world

```js:hello.js
const msg = 'hello world';
console.log(msg);
```

```bat
node ./hello.js

=> hello world
```

### 変数の型(es6)

今回使用するもののみピックアップ。

#### Number
数値型。C#やPython等では整数型(Int)、浮動小数点型(Float)等あったがjsはこれだけ

```js
const pi = 3.14;
var index = 1;
```

#### String
文字列型

```js
const message = 'hello world'; // '' で囲む
var name = "huga";             // "" もOK
```

#### Boolean
`true` or `false`

```js
const flg = true;
console.log(flg);       // => true
console.log(!flg);      // => false

if(flg) {
    console.log('flg is true'); // => flg is true
}
```

#### Array(配列)
配列。どちらかというとリストに近いような...

```js
// 宣言方法
const arr = [];                   // arr => [](空配列)
let values = new Array();         // values => [](空配列)

const arr2 = [1,2,3];             // arr2 => [1,2,3]
const values2 = new Array(5);     // values => [ <5 empty items> ] (5要素の配列)
console.log(values2[1]);          // => undefined

// 要素の追加
arr.push(1);
arr.push(2);
console.log(arr);                 // => [1,2]

// 要素の参照
console.log(arr[0]);              // => 1
console.log(arr[1]);              // => 2

// 要素の取り出し
console.log(arr.pop());           // => 2
console.log(arr);                 // => [1]
```

#### Function
関数

```js
// 通常?の宣言
function twice(num) {
    return num * 2;
}

// ラムダ式
const joinStr = (str1, str2) => { return str1 + str2; };

console.log(twice(2));                      // 4
console.log(joinStr("hello ", "world"));    // hello world
```

#### Object
オブジェクト、連想配列。classとかもes5ではオブジェクトになる?? keyに対して任意のデータを割り当てる

```js

// 宣言
const obj = {};
const obj2 = new Object();

// 要素の追加
obj["id"] = 1;
obj.name = "huga";
obj.getName = function() {
    return this.name;
};
console.log(obj);               // => { id: 1, name: 'huga', getName: [Function] }

// 要素の参照
console.log(obj["id"]);         // => 1
console.log(obj.name);          // => huga
console.log(obj.getName());     // => huga
```

#### null, undefined
null は他の言語と大体同じ?参照先が未割当の状態。
undefined はそもそも項目すらない状態?(よくわかってません)
今回はここを理解していなくてもできるようにやっていきます。

#### const, let, var

`var`
変数を宣言し、ある値に初期化することもできる。__グローバル変数として定義される__。

`let`
ブロックスコープのローカル変数を宣言し、ある値に初期化することもできる。
ローカル変数(スコープ内で有効な変数)で、再代入が可能。

`const`
読み取り専用の名前付き定数を宣言する。
ローカル変数(スコープ内で有効な変数)で、再代入が不可能。

### 制御構文
#### if
分岐処理

```js
let flg = false;

if (flg) {
    // 実行されない
}
else {
    // 実行される
}

if (flg != true) {
    // 実行される
}
```

#### for
繰り返し処理

```js
const numbers = [1,2,3,4,5];

for (let i = 0; i < numbers.length; i++) {
    const element = numbers[i];
    console.log(element);
    // => 1
    // => 2
    // => 3
    // => 4
    // => 5
}
```

他にも`switch`, `while` 等あります。

参考リンク
- [文法とデータ型(MDN)](https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Grammar_and_types)
- [ES2015(ES6) 入門](https://qiita.com/soarflat/items/b251caf9cb59b72beb9b)


## 4. HTML
適当なフォルダをvscodeで開いて、`適当な名前.html`というファイルを作成します。
ファイル内で `html` と入力すると以下のように補完が表示されると思いますので、`html5`を選択します。

![](2019-11-16-07-26-50.png)

以下のようにテンプレートが挿入されます。

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>

</body>
</html>
```

`body` タグの中にHTMLを記述していきます。

### Header

見出しです。h1 ～ h6まであり、数字が小さい程基本的には表示が大きくなります。
bodyタグの中に記述して確認してみます。

```html
<body>
    <h1>h1 ヘッダーです</h1>
    <h2>h2 ヘッダーです</h2>
    <h3>h3 ヘッダーです</h3>
    <h4>h4 ヘッダーです</h4>
    <h5>h5 ヘッダーです</h5>
    <h6>h6 ヘッダーです</h6>
</body>
```

〇結果

<div>
    <h1>h1 ヘッダーです</h1>
    <h2>h2 ヘッダーです</h2>
    <h3>h3 ヘッダーです</h3>
    <h4>h4 ヘッダーです</h4>
    <h5>h5 ヘッダーです</h5>
    <h6>h6 ヘッダーです</h6>
</div>

<!-- ![](2019-11-16-07-35-11.png) -->

### リスト
リストを表示します。番号付き(`<ol></ol>`)と番号無し(`<ul></ul>`)があります。

```html
<body>
    <!-- 番号付きリスト -->
    <ol>
        <li>リスト1</li>
        <li>リスト2</li>
        <li>リスト3</li>
        <li>リスト4</li>
    </ol>

    <!-- 番号無しリスト -->
    <ul>
        <li>リスト1</li>
        <li>リスト2</li>
        <li>リスト3</li>
        <li>リスト4</li>
    </ul>
</body>
```
〇 結果

<div>
    <ol>
        <li>リスト1</li>
        <li>リスト2</li>
        <li>リスト3</li>
        <li>リスト4</li>
    </ol>
</div>
<div>
    <ul>
        <li>リスト1</li>
        <li>リスト2</li>
        <li>リスト3</li>
        <li>リスト4</li>
    </ul>
</div>

<!-- ![](2019-11-16-07-46-04.png) -->

### 表
表を表示します。

```html
<body>
    <table border="1">
        <tr><th>項目1</th><td>A</td></tr>
        <tr><th>項目2</th><td>B</td></tr>
        <tr><th>項目3</th><td>C</td></tr>
    </table>

    <hr>

    <table border="1">
        <thead>
            <tr><th>キー</th><th>値</th></tr>
        </thead>
        <tbody>
            <tr><td>項目1</td><td>A</td></tr>
            <tr><td>項目2</td><td>B</td></tr>
            <tr><td>項目3</td><td>C</td></tr>
        </tbody>
    </table>
</body>
```

〇 結果

<div>
    <table border="1">
        <tr><th>項目1</th><td>A</td></tr>
        <tr><th>項目2</th><td>B</td></tr>
        <tr><th>項目3</th><td>C</td></tr>
    </table>
</div>

<div><hr></div>

<div>
    <table border="1">
        <thead>
            <tr><th>キー</th><th>値</th></tr>
        </thead>
        <tbody>
            <tr><td>項目1</td><td>A</td></tr>
            <tr><td>項目2</td><td>B</td></tr>
            <tr><td>項目3</td><td>C</td></tr>
        </tbody>
    </table>
</div>

<!-- ![](2019-11-16-07-51-29.png) -->



### 入力要素
テキストボックスやボタン、スライダー等。
通常は`<form></form>`タグの中で使用してGETやPOSTメソッドで送信に使いますが、
Vue等のフレームワークでは変数の操作に用いたりします。

```html
<body>
    <div>
        <h3> テキストボックス </h3>
        <input type="text">
    </div>

    <div>
        <h3> テキストボックス(パスワード) </h3>
        <input type="password">
    </div>

    <div>
        <h3> ボタン </h3>
        <input type="button" value="ボタンです">
    </div>

    <div>
        <h3> チェックボックス </h3>
        チェックしてください<input type="checkbox">
    </div>

    <div>
        <h3> ラジオボックス </h3>
        <input type="radio" name="my-radio" value="radio-item1">ラジオ1
        <input type="radio" name="my-radio" value="radio-item2">ラジオ2
        <input type="radio" name="my-radio" value="radio-item3">ラジオ3
    </div>

    <div>
        <h3> コンボボックス </h3>
        <select>
            <option selected="0">選択アイテム1</option>
            <option>選択アイテム2</option>
            <option>選択アイテム3</option>
            <option>選択アイテム4</option>
        </select>
    </div>

    <div>
        <h3>スライダー</h3>
        <input type="range">
    </div>

    <div>
        <h3> カラーピッカー </h3>
        <input type="color">
    </div>

    <div>
        <h3> FILE API </h3>
        <input type="file" id="file-handler">
    </div>

</body>
```

〇結果 (Chrome)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117696/59846969-bd4a-2be6-571b-97390a1eac12.png)



## 4. Vue入門

### 準備

Webアプリケーション フレームワークのVueを使用して作成していきます。

〇 参考リンク:
- [Vue.js(本家)](https://jp.vuejs.org/index.html)
- [基礎から学ぶ Vue.js(猫本サイト)](https://cr-vue.mio3io.com/)


まずはHTMLファイルとスタンドアロン版のVue.js(CDN)を使用してVueの基本構文を確認します。
フォルダ内に`vue-starter.html`を作成し、以下のように入力します。

```html:vue-starter.html
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="utf-8">
        <title>Vue Starter</title>
    </head>
    <body>
        <div id="app">
            <!-- このエリアにVue 形式の HTMLを記述していく(描画DOMエリア) -->
        </div>

        <!-- vue.js を読み込んでからVueオブジェクトを記載していく -->
        <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
        <script>
            // Vue オブジェクト
            var app = new Vue({
                el: '#app',
                data() {
                    return {
                        // object 形式でプロパティを記述していく
                    };
                },
                // 算出プロパティ(キャッシュされる)
                computed: {

                },
                // 監視プロパティ
                watch: {

                },
                // 関数(キャッシュされない、都度評価される)
                methods: {

                },

                // ライフサイクルフック ---------------------------------------------------
                // https://jp.vuejs.org/v2/api/#beforeUpdate
                beforeCreate() {

                },
                created() {

                },
                mounted() {

                },
                beforeUpdate() {

                },
                updated() {

                },
                activated() {

                },
                deactivated() {

                },
                beforeDestroy() {

                },
                destroyed() {

                }
                // ライフサイクルフック ---------------------------------------------------
            });
        </script>
    </body>
</html>
```

|プロパティ名|説明|
|:-:|:-:|
|el|VueでレンダリングするHTML DOMのidを記述する。|
|data|レンダリングで参照されるプロパティ。画面描画のもとになる|
|computed|dataを元に算出されるプロパティ。いわゆるgetter|
|watch|登録されているdataの変化を監視し、変更があった場合の処理を記述する|
|methods|関数。ボタンのクリックイベント等、特定のイベントにフックして実行する処理を記述する|

#### ライフサイクルフック
Vueが描画を行う際の特定のタイミングで呼ばれるイベント。
詳しくは以下参照。

- [ライフサイクルダイアグラム](https://jp.vuejs.org/v2/guide/instance.html#ライフサイクルダイアグラム)


### 宣言的レンダリング(プロパティの参照)

描画DOMエリアとVueオブジェクトを以下のように修正します。

```html
<div id="app">
    <!-- このエリアにVue 形式の HTMLを記述していく(描画DOMエリア) -->
    <p>{{message}}</p>
</div>
```

```js
// Vue オブジェクト
var app = new Vue({
    el: '#app',
    data() {
        return {
            // object 形式でプロパティを記述していく
            message: 'Hello Vue!'
        };
    }
});
```

〇結果

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117696/38a62f0e-09a2-4083-1bff-6ac05a8f17de.png)

### 繰り返しレンダリング(v-for)
配列やオブジェクトの要素を繰り返しレンダリングします。
描画DOMエリアとVueオブジェクトを以下のように修正します。

```html
<div id="app">
    <!-- このエリアにVue 形式の HTMLを記述していく(描画DOMエリア) -->
    <p>Array のレンダリング</p>
    <ul>
        <li v-for="(item, index) in itemList">{{index}}: {{item}}</li>
    </ul>

    <p>Object のレンダリング</p>
    <table border="1">
        <thead>
            <th>key</th><th>value</th>
        </thead>
        <tbody>
            <tr v-for="(item, key) in obj"><td>{{key}}</td><td>{{item}}</td></tr>
        </tbody>
    </table>
</div>
```

```js
data() {
    return {
        // object 形式でプロパティを記述していく
        itemList: [
        'huga',
        'hoge',
        'poyo'
        ],
        obj: {
            name: 'Vue',
            id: 1,
            lang: 'JavaScript'
        }
    };
},
```

〇結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117696/954c4fa0-be34-043a-256e-80ad8b665186.png)


### イベント(v-on)
ボタンクリック等のイベントをフックして処理を実行します。
描画DOMエリアとVueオブジェクトを以下のように修正します。

```html

<div id="app">
    <!-- このエリアにVue 形式の HTMLを記述していく(描画DOMエリア) -->
    <button v-on:click="call()">click me!!</button>
</div>
```

```js
methods: {
    call() {
        alert('button clicked');
    }
},
```

〇結果
ボタンをクリックするとアラートが表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117696/be6086e6-771f-8c99-e899-ba7a783ab3e6.png)

### 条件付きレンダリング(v-if, v-show)
`v-if`、`v-show`共に条件を満たす場合にのみレンダリングされます。
違いは、レンダリングされない場合、以下の点が異なる。
- `v-if`: レンダリングされない場合、DOMが生成されない。
- `v-show`: レンダリングされない場合もDOMが生成される。style属性が`display: none;`となることで表示されなくなる。

描画DOMエリアとVueオブジェクトを以下のように修正します。

```html
<div id="app">
    <!-- このエリアにVue 形式の HTMLを記述していく(描画DOMエリア) -->
    <button @click="toggleFlg()">フラグ切り替え</button>
    <p v-if="isShow == true">フラグがtrue なら描画されます</p>
</div>
```

```js
data() {
    return {
        // object 形式でプロパティを記述していく
        isShow: true
    };
},
// 関数(キャッシュされない、都度評価される)
methods: {
    toggleFlg() {
        this.isShow = !this.isShow;
    }
}
```

○結果
ボタンを押すたびに下部のテキストの表示、非表示が切り替わります。

### 算出プロパティと監視プロパティ(computed,watch)
描画DOMエリアとVueオブジェクトを以下のように修正します。

```html
<div id="app">
    <!-- このエリアにVue 形式の HTMLを記述していく(描画DOMエリア) -->
    <h2>computed</h2>
    <p>num is: {{num}}</p>
    <p>twice is: {{twice}}</p>
    <button @click="increment()">numへ1加算</button>

    <h2>watch</h2>
    <p>{{watchStr}}</p>
</div>
```

```js
data() {
    return {
        // object 形式でプロパティを記述していく
        num: 1,
        watchStr: ""
    };
},
// 算出プロパティ(キャッシュされる)
computed: {
    twice() {
        // 二乗の数を返す
        return Math.pow(this.num, 2);
    }
},
// 関数(キャッシュされない、都度評価される)
methods: {
    increment() {
        this.num++;
    }
},
// 監視プロパティ
watch: {
    // 関数名は監視するプロパティ名
    num(newValue, oldValue) {
        this.watchStr = `${oldValue} -> ${newValue}`;
    }
},
```

○結果
<img width="308" alt="Vue_Starter.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117696/91bc6cad-a309-f6fe-b81e-dd465755913a.png">

### form要素との連携(v-bind、v-model)
`input`タグのform要素と同期して`data`を操作できます。


html部

```html
<div id="app">
    <!-- このエリアにVue 形式の HTMLを記述していく(描画DOMエリア) -->
    <h2>v-model(双方向同期)</h2>
    <input type="text" v-model="message">
    <p>{{message}}</p>

    <select v-on:input="selectedItem = $event.target.value;">
        <option selected="0" v-for="item in listItems">{{item}}</option>
    </select>
    <p>入力値:{{selectedItem}}</p>

    <!-- v-bind:r="radius" は :r="radius" と省略してもOK -->
    <h2>v-bind(片方向同期)</h2>
    <input type="range" v-model="radius" max="100" min="0">
    <p>radius = {{radius}}</p>
    <svg viewbox="0 0 300 300" width="300" height="300">
        <circle v-bind:r="radius" cx=150 cy=150></circle>
    </svg>
</div>
```

JavaScript部

```js
data() {
    return {
        // object 形式でプロパティを記述していく
        message: 'hello vue',
        selectedItem: null,
        listItems: [
            "item1",
            "item2",
            "item3",
        ],
        radius: 10,
    };
},
```
○結果

<img width="401" alt="Vue_Starter.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117696/f31ed805-05ce-2cb6-7951-2d8bfd46e8f1.png">


## Vue CLI を使ったWebアプリ(SPA)の作成
ここからfront-end、back-end に分けてWebアプリ（のようなもの）を作っていきます。
まずは、front-end側から作り始めます。

### プロジェクトフォルダ作成
お好きなところに`vue-spa-sample`という名前でフォルダを作ります。

### vue/cliのインストール
`vue-spa-sample`フォルダ内でコマンドラインを立ち上げます。
以下のコマンドでVue CLI をグローバルへインストールします。

```bat
npm i -g @vue/cli
```
なお、npm のコマンドは以下が纏まっていてみやすかったです。
[npm 入門](http://www.tohoho-web.com/ex/npm.html)

インストールが終了したら以下のコマンドでバージョンが返ってくることを確認します。

```bat
vue -V
```

### front-end プロジェクトの作成
続けてコマンドラインから

```bat
vue create front
```

でプロジェクトを作成します。
`front` の部分はプロジェクト名になるので、
本来はお好きな名前をつけていただいてOKです。

__[2019/11/18 修正]__

axios が上手く動かなかったので、TypeScript で記述して行きます。
原因わかりましたらまた更新します。

以前のここのセットアップ方法はJavaScript版のものなので、
TypeScript版へ修正します。

__[2019/11/20 修正]__
JavaScript版でも動作確認できました。
JavaScript版でこのハンズオンを進める場合は、以下の`Please pick a preset:` で`default(babel, eslint)`を選択します。（選択するとプロジェクトのダウンロードがすぐに始まります）。
TypeScriptを使用する場合は以下の流れで設定して行きます。


<img width="261" alt="pechi_—_node____nvm_versions_node_v12_13_0_bin_vue_create_test_—_80×24.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117696/bf179677-a397-f95a-8f1e-33dac6cc15b1.png">
↓キーを押下して `Manually select features` を選択します。


<img width="566" alt="pechi_—_node____nvm_versions_node_v12_13_0_bin_vue_create_test_—_80×24.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117696/e1666c02-b281-2da9-b9a4-2fd2bfb4fcb5.png">

`Babel` 、 `TypeScript` 、 `Linter / Formatter` を選択し、Enterを押下します。
ちなみに `Babel` -> 1 、 `TypeScript` -> 2 のように、
数字キーがそれぞれの要素に対応しています。

<img width="565" alt="pechi_—_node____nvm_versions_node_v12_13_0_bin_vue_create_test_—_80×24.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117696/3cea2ed6-9fa4-ab6e-60f5-ff341ae50cf6.png">

クラススタイルで記述するか？の質問です。 __n__ を押下します。
クラススタイルは記述しやすいですが、以下のデメリットがあるため、私は通常の記述スタイルをおすすめしています。

- `<template>` タグ内で補完が効かない（vscode,vetur 使用の場合）
- 挙動が異なる場合がある
- ググってヒットする情報のほとんどがJavaScriptの記述方式であり、クラススタイルを採用した場合は読み換える必要がある。

<img width="568" alt="pechi_—_node____nvm_versions_node_v12_13_0_bin_vue_create_test_—_80×24.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117696/089beaef-bfb5-a357-cd94-db6ffc3b79e5.png">

y を押下

<img width="561" alt="pechi_—_node____nvm_versions_node_v12_13_0_bin_vue_create_test_—_80×24.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117696/3162fa87-f8a4-8b63-3353-6deed89b12db.png">

`ESLint with error prevention only` を選択して押下

<img width="571" alt="pechi_—_node____nvm_versions_node_v12_13_0_bin_vue_create_test_—_80×24_と_「Vue_Web_App_Tutorial」を編集_-_Qiita.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117696/406dfcb9-31eb-580c-36ff-d72bff98dd3a.png">

`Lint on save` を選択して押下

<img width="569" alt="pechi_—_node____nvm_versions_node_v12_13_0_bin_vue_create_test_—_80×24_と_「Vue_Web_App_Tutorial」を編集_-_Qiita.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117696/a5058ade-c41b-a5ce-76e4-d2d426acab58.png">

`In dedicated config files` を選択して押下

<img width="570" alt="pechi_—_node____nvm_versions_node_v12_13_0_bin_vue_create_test_—_80×24_と_「Vue_Web_App_Tutorial」を編集_-_Qiita.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117696/a293476f-54ae-34aa-5aa2-36b608571331.png">

n を選択して押下。

パッケージのダウンロードが始まります。
全て終了したら、表示されているように以下のコマンドを入力してdev serverを立ち上げます。

```bat
cd front
npm run serve
```

dev server が立ち上がったら、ブラウザから`http://localhost:8080`へ接続します。
以下のような画面が表示されます。

<img width="1439" alt="front_と_front_—_node_◂_npm_TERM_PROGRAM_Apple_Terminal_NVM_CD_FLAGS__TERM_xterm-256color_—_80×24.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117696/9dc44643-1e0b-9125-c9a0-a910239f7be2.png">

確認したら、vscodeでfrontフォルダを開きます。
これからVueのSFCファイルを見て行きますが、ハイライトや補完等の機能が使えるように、
エクステンションで`vetur`を検索し、インストールしておきます。

### 単一ファイルコンポーネント(SFC)とコンポーネント
Vue CLI で作られたプロジェクトはSFCに対応しています。
SFCとは、コンポーネント(Vueの描画領域を部分的に記述し、部品化したもの)を`template`、`script`、`css`のそれぞれのタグに分けて記述したものです。

現在のプロジェクトで言うと、`src/App.vue`が画面全体を描画しているコンポーネントで、
`src/components/HelloWorld.vue`は部分的リンクの部分を描画している部品になります。

コードで見るとこんな感じです。

<img alt="全画面_2019_11_16_15_23.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117696/8e469691-5085-1acf-8434-9be203f10b37.png">

### コンポーネントを作ってみる
`src/components/`内に`MainPage.vue`という名前でSFCを作ります。
以下のようにコードを記述します。
(以降のコードはTypeScriptを選択した場合の記述スタイルです。JavaScriptを選択した場合は、
`<script lang="ts">` を `<script>` に変更してください。それ以外の部分は共通で動作します。)

```vue:src/components/MainPage.vue
<template>
  <div>
      <h1>自作コンポーネントのページです</h1>
      <h2>hello {{name}}</h2>
  </div>
</template>

<!-- JavaScriptの場合は以下のように lang="ts" を記述しない-->
<!-- <script> -->
<script lang="ts">
import Vue from 'vue';

export default Vue.extend({
    name: 'main-page',
    data() {
        return {
            name: 'vue', // 好きな名前を入れてください
        }
    }
});
</script>

<style>

</style>

```

ファイルを保存したら、`src/App.vue`を以下のように修正します。

```vue:src/App.vue
<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png">
    <!-- コメント化 -->
    <!-- <HelloWorld msg="Welcome to Your Vue.js App"/> -->
    <!-- 追加 -->
    <main-page/>
  </div>
</template>

<script lang="ts">
// import HelloWorld from './components/HelloWorld.vue' // <- コメント化
import MainPage from "./components/MainPage.vue"; // <- 追加、.vue を忘れないで

export default {
  name: 'app',
  components: {
    // HelloWorld // コメント化
    MainPage // 追加
  }
}
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

```

ファイルを保存します。
再度先ほどブラウザで開いた`http://localhost:8080`を見るとオートリロードがかかって
以下のようになっていると思います。（なっていなかったら手動でリロードしてください）

<img width="1437" alt="front_と_App_vue_—_front.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117696/7089968b-3946-2669-a19c-e5079ccf3882.png">

#### MyButton コンポーネントの追加
`src/components/`内に`MyButton.vue`という名前でオリジナルのボタンを作って見ます。
私はデザインはできないので、以下のサイト様から~~パク~~参考にさせて頂きました。
[CSSで作る！押したくなるボタンデザイン100（Web用）](https://saruwakakun.com/html-css/reference/buttons)

```vue:src/components/MyButton.vue
<template>
  <a href="#" class="btn-square" @click="change()">{{label}}</a>
</template>

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

export default Vue.extend({
    name: 'my-button',
    props: [
        'label'
    ],
    methods: {
        change() {
            this.$emit('label-update', 'changed');
        }
    }
})
</script>

<style scoped>
.btn-square {
  display: inline-block;
  padding: 0.5em 1em;
  text-decoration: none;
  background: #668ad8;/*ボタン色*/
  color: #FFF;
  border-bottom: solid 4px #627295;
  border-radius: 3px;
}
.btn-square:active {
  /*ボタンを押したとき*/
  -webkit-transform: translateY(4px);
  transform: translateY(4px);/*下に動く*/
  border-bottom: none;/*線を消す*/
}
</style>
```

`style` タグ内に付いている`scoped`は、SFCファイル独自のもので、
記述すると定義されているCSSをこのコンポーネントのみに適用することができます。

また、Vueオブジェクト内の`props`は、コンポーネントの呼び出し側からデータを受け取ることのできるプロパティです。また、methods内の`this.$emit`は、このコンポーネント呼び出すイベントを定義しています。
具体的には、ボタンが押下されると、`label-update`イベントが起き、呼び出し元の親コンポーネントへ変更された値を通知します。
(Vueでは子コンポーネントがporpを勝手に書き換えることはNGとされており、このようにイベントとして親コンポーネントに通知を送り、親オブジェクトに変更をかけてもらうようにしています。)

MainPage.vueを以下のように変更し、結果を確認して見ます。

```vue:src/components/MainPage.vue
<template>
  <div>
      <h1>自作コンポーネントのページです</h1>
      <h2>hello {{name}}</h2>
      <!-- 追加   -->
      <my-button :label="btnName" @label-update="updateName"/>
  </div>
</template>

<script lang="ts">
import Vue from 'vue';
import MyButton from "./MyButton.vue"; // 追加

export default Vue.extend({
    name: 'main-page',
    components: {
        MyButton
    },
    data() {
        return {
            name: 'vue', // 好きな名前を入れてください
            btnName: 'Myボタン' // 追加
        }
    },
    // 追加
    methods: {
        updateName(val: string) {
            this.btnName = val;
        }
    }
});
</script>

<style>

</style>
```

<img width="1426" alt="front_と_MainPage_vue_—_front.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117696/01756850-4d99-b121-0049-e262a50ea8ed.png">



## 5. WebAPIの作成
express を使ってWebAPIを作成します。
`vue-spa-sample`フォルダ内に`back`フォルダを作り、その中でコマンドラインを立ち上げます。
以下のコマンドを順に打ち込んで行きます。

```bat
npm init -y
npm i -S express cors body-parser
```

インストールが終わったら、vscodeで`back`フォルダを開きます。
`back/src`フォルダ内に`router`フォルダを作成し、その中に`root.js`ファイルを作成します。

```js:src/router/root.js
const express = require("express");
const router = express.Router();

router.get('/', (req/* リクエスト */, res/* レスポンス */) => {
    
    // json でレスポンス
    res.send({
        message: 'res from express'
    });
});

module.exports = router;
```

`back/src`フォルダ内に`app.js`ファイルを作成し、以下のコードを記述します。

```js:src/app.js
const express = require("express");
const bodyparser = require("body-parser");
const cors = require("cors");
const indexRouter = require("./router/root");


const app = express();

// CORS 制限の解除
app.use(cors());

// 通信にJsonを使用する
app.use(bodyparser.json())

// /にindexRouterをルーティング
app.use('/', indexRouter);


module.exports = app; // appを公開
```

`back/src`フォルダ内に`index.js`ファイルを作成し、以下のコードを記述します。

```js:src/index.js
const app = require("./app");

const port = 3000;

app.listen(port, () => {
    // port listen を始めた時に実行される処理
    console.log(`express listen port ${port}`);
});
```

全て保存したら、コマンドラインで以下のコマンドを実行

```bat
node ./src/index.js
```

サーバーが起動したら、ブラウザから`http://localhost:3000`にアクセスする。
以下のようになったらOK。

<img width="364" alt="localhost_3000.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117696/17a70e59-7164-35ad-4767-a4caa0ddc8de.png">


## 6 WebアプリとWebAPI で通信
これまで作ってきたfront-end と back-end を連携して行きます。

front側のdev-serverを`ctrl + c`で終了し、以下のコマンドを入力する

```bat
npm i -S axios
```
axiosがインストールされたら、再度

```bat
npm run serve
```

でdev-server を起動して起きます。

### axiosで通信する
`src/components/MainPage.vue` を以下のように修正します。

```vue:src/components/MainPage.vue
<template>
  <div>
      <h1>自作コンポーネントのページです</h1>
      <h2>hello {{name}}</h2>
      <my-button :label="btnName" @label-update="updateName"/>

      <!-- 追加   -->
      <div>
          <p>{{message}}</p>
          <button @click="getExpress()">get express root</button>
      </div>
  </div>
</template>

<script lang="ts">
import Vue from 'vue';
import MyButton from "./MyButton.vue";
import * as axios from "axios";

export default Vue.extend({
    name: 'main-page',
    components: {
        MyButton
    },
    data() {
        return {
            name: 'vue', // 好きな名前を入れてください
            btnName: 'Myボタン',
            message: 'no message'  // 追加
        }
    },
    methods: {
        updateName(val: string) {
            this.btnName = val;
        },
        // 追加
        getExpress() {
            axios.default.get('http://localhost:3000').then((responce) => {
                this.message = responce.data.message;
            });
        }
    }
});
</script>



<style>

</style>
```

`http://localhost:8080`にアクセスして`get express root`のボタンを推してみる。
`no message` が res `from express` に変わったら成功。localのexpressサーバーと通信できてます。


<img alt="hangouts_google_com_が画面を共有しています。_と_front2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117696/30fd4a8e-a877-1008-115d-23171c374f79.png">



## 今後やって行くこと
- Electronでデスクトップアプリ化 [Electron+Vue.jsを使ったデスクトップアプリ開発を始める手順](https://www.virment.com/vue-electron/)
- CSS フレームワークを使って綺麗なデザインにする [Vue-cli3からのVuetifyを試す](https://qiita.com/satoysan/items/1dc6558231ad5bf17717)
- python でwebサーバーを立てる [ゾンビでもわかるPythonプログラミング](http://python.zombie-hunting-club.com/entry/2017/11/03/223503)
- 無料WebAPIを叩いてみる[個人でも使える！おすすめAPI一覧](https://qiita.com/mikan3rd/items/ba4737023f08bb2ca161)




****