Vue.js勉強会に参加してみた

## はじめに
WantedlyでVue.jsの勉強会を見つけたので、参加してきました。
Vue.jsは前々から勉強しようと思っていたのですが、後回しになっていたので、これを機会に本腰を入れよう！

## 環境構築
今回、事前に環境構築をしてきてとお願いされたのは以下のもの

- Visual Studio Code
- 拡張機能でVetur(Vue.js用の拡張機能)もインストール
- githubアカウント
- Node.js
    - versionは10以上
- git
    - versionは2.20以上

このうち、(僕のパソコンはWindowsなこともあり)Node.jsとgitのインストールを行いました。

### nodistのインストール
WindowsでNode.jsのバージョンを管理するには、nodistを使うらしい。
インターン先ではnodebrewを使っていたので、「あれ？」と思ったが、nodebrewは基本的にmac環境で使っているそうです。

以下のサイトの手順通りに進めました。
[[Node.js] Node.js の導入(Windows編)](https://qiita.com/ksh-fthr/items/fc8b015a066a36a40dc2)

#### 1. 公式ページにアクセス

#### 2. インストーラーをダウンロード
実行時のversionは0.9.1

#### 3. インストーラーに従ってインストール
ここでインストール終了時に「PATH not updated, original length 1183 > 1024」というエラーが。
取り敢えず、「OK」を押すと、何事もなかったかのように「Install Finished」と出る。

しかしコマンドプロンプトを開いて、`$ nodist -v`と打っても

```
'nodist'は、内部ファイルまたは～
```

というエラーが返ってくる。これはPATHが通ってないときに出てくるやつですね。
まぁ、さっきの時点でそんな気はしましたが、、、

#### 4. nodistのPATHを通す
こちらの方の記事を参考にしたら、うまくいきました！
[node.js インストール備忘録（windows7）](https://qiita.com/yokoh9/items/20d6bdc6030a3a861189)

#### 5. nodistのversion確認
```
$ nodist -v
0.9.1
```
無事インストールできました！

### Node.jsのインストール
続いてNode.jsをインストールしていきます。

#### 1. インストール可能なNode.jsのバージョンを確認
```
$ nodist dist
```
めちゃくちゃ出てきてビックリ（笑）。<br>

#### 2. 任意のバージョンをインストール
「偶数バージョンの方がいい」みたいなことを聞いたことがあったので、調べてみると以下のような記事が。
[node.jsのバージョンごとの違い](https://qiita.com/kawak/items/64e4d6cb25f0494f604d)

今回は安全に使いたいので、偶数バージョンの最新版(12.14.1)をインストール。

```
$ nodist + v12.14.1
```

#### 3. インストールされている Node.js を確認
```
$ nodist
  (x64)
> 12.14.1
```

#### 4. Node.js のバージョンを切り替える
```
$ nodist v12.14.1
```

#### 5. 現在使用可能なNode.jsのバージョンを確認
```
$ node -v
v12.14.1
```

#### 6. npmのアップデート
npmはNode.jsのパッケージを管理するもの。

```
$ npm -v
$ npm install npm -g
```

以上でNode.jsのインストールは終了！

### Gitのインストール
今までGitを使いたいときはSourseTreeを使っていたのですが、インターン先ではGitをコマンドで打つので、自分のパソコンにも入れたいと思いつつ、後回しにしていた。。。
今回やっとGitをインストール！

以下の記事を参考にしました。
[Git インストール for Windows](https://qiita.com/andna0410/items/c9b8e232d4aa9ac77584)

特に問題はなく成功

```
$ git --version
git version 2.25.0.windows.1
```

## Vueとは
- オープンソース
- WebアプリケーションのUIを構築
- SPAを高速に構築することが可能
- 学習コスト低い

## Gitを使ってみる
ここは今回のメインではないので、箇条書きで進めます

1. GitHubでリポジトリを作る
2. リモートに適当なディレクトリを作る
3. README.mdを作成し、`# Hello, GitHub!` と入力して保存
4. `git init` をして、gitの初期化
5. `git remote add origin git@github.com:xxxx/yyyy.git`
    - `git remote -v` でリモートを確認できる
6. `git add .` をして、先ほど編集した差分をstage (commit待ちのファイル群) に乗せる
7. `git status` で、stageに乗ったか確認 (緑色になってたらstaged)
8. `git commit -m "first commit"` で、差分をコミット
9. `git push origin master` で、作ったコミットをoriginリモートのmasterブランチ (デフォルトのブランチ) にpush

## Vue.jsについて
Vue.jsはドキュメントがとても優秀、わからなくなったらここを読めばほとんど全て理解できる
- [https://jp.vuejs.org/v2/guide/index.html](https://jp.vuejs.org/v2/guide/index.html)
- ~~そのドキュメントの日本語がよく分からず後回しにした人間がここに1人~~

### Vueで書いてみよう
今回はscriptタグで、CDNからVueを読みます。
Todoリストを作成して、練習していきます。

```html:sample.html
<!DOCTYPE html>
<html lang="ja">

<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>Step1 ToDo Application</title>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>

</html>
```

#### Vueインスタンスの作成
ではbody要素を足していきます。
まずはVueのインスタンスを作ります。更に、elプロパティでappというidの要素を指定すると、それ以下をVueが管理してくれます。

```html:sample.html
<body>
  <div id="app">
    <!-- ここをvueが管理 -->
  </div>
  <script>
    const app = new Vue({
      el: '#app'    // "id=app"を指定
    })
  </script>
</body>
```

#### データを持つ
動的に変化するデータを扱うには、data プロパティに宣言します。
そして{{}}内に囲むことで、そのデータを表示できます。

```html:sample.html
<body>
  <div id="app">
    {{ hoge }}
  </div>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        hoge: "sample"
      }
    })
  </script>
</body>
```

ここでVueの特徴の一つreactive性が出てきます。
これは、変更されたdataが直ちに再描画されるというものです。

#### フォームデータの束縛(バインディング)
`v-model="~"`の形で記述された場合、`~`の変数にデータを格納します。

```html:sample.html
<body>
  <div id="app">
    <label>ToDoの内容<input type="text" v-model="todoText"></label>
    {{ todoText }}
  </div>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        todoText: "sample"
      }
    })
  </script>
</body>
```

こうすると、inputタグのデータがtodoTextという変数と結びつくため、フォーム内に初めから"sample"という文字列が表示されます。

#### リストの表示
リストを表示するには`v-for`を使います。

```html:sample.html
<body>
  <div id="app">
    <label>ToDoの内容<input type="text" v-model="todoText"></label>
    <ul>
      <li v-for="todo in todoList">{{ todo }}</li>
    </ul>
  </div>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        todoText: "sample",
        todoList: ['掃除', '洗濯', '料理']
      }
    })
  </script>
</body>
```

#### 算出プロパティ
テンプレートで表示するのに計算が必要な場合は算出プロパティ`computed`を使用します。

```html:sample.html
<body>
  <div id="app">
    <label>ToDoの内容<input type="text" v-model="todoText"></label>
    <p>あと{{ todoCount }}個のtodo</p>
    <ul>
      <li v-for="todo in todoList">{{ todo }}</li>
    </ul>
  </div>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        todoText: "sample",
        todoList: ['掃除', '洗濯', '料理']
      },
      computed: {
        todoCount() {
          return this.todoList.length
        }
      }
    })
  </script>
</body>
```

#### メソッド
DOMイベントに対して何かのアクションをするには、`v-on`ディレクティブを使用します。
これにより、呼び出したいメソッドを呼ぶことができます。

```html:sample.html
<body>
  <div id="app">
    <label>ToDoの内容<input type="text" v-model="todoText"></label>
    <button v-on:click="register">登録</button>
    <p>あと{{ todoCount }}個のtodo</p>
    <ul>
      <li v-for="todo in todoList">{{ todo }}</li>
    </ul>
  </div>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        todoText: "",
        todoList: []
      },
      computed: {
        todoCount() {
          return this.todoList.length
        }
      },
      methods: {
        register: function() {
          this.todoList.push(this.todoText);
          this.todoText = "";
        }
      }
    })
  </script>
</body>
```

### より本格的な開発のために
#### コンポーネントシステム
コンポーネントとは、ざっくり言うと「部品」のこと。
ページをコンポーネント化して、各部品として管理することで、再利用性や保守性が高まる。

Vueでは.vueファイルとして保存します。
更にVueの特徴は、.vueファイル一枚にtemplate,style,scriptを書けることです。(単一ファイルコンポーネント)
このおかげで、ファイル管理が楽になります。

#### Vuex
状態管理のためのライブラリ(+パターン)。
共通の状態やデータを複数のコンポーネントで管理する場合に活用できる。

参考:[ざっくり理解、Vuexって何？VuexとAPIの関係を図解してみた](https://qiita.com/MatakiTanaka09/items/8cdccf54164f782ad2e8)

## Nuxt.jsについて
### Nuxt.jsとは
> Nuxtは、モダンな web アプリケーションを作成する Vue.js に基づいたプログレッシブフレームワークです。Vue.js 公式ライブラリ（vue、vue-router や vuex）および強力な開発ツール（webpack、Babel や PostCSS）に基づいています。 Nuxt の目標は、優れた開発者エクスペリエンスを念頭に置き、Web 開発を強力かつ高性能にすることです。

先ほど説明したものや、cssフレームワーク、SSRなどもろもろのベストプラクティスを適用したプロジェクトを一瞬で構築可能。
本格的なWebアプリケーション作成ができる。

### Nuxt.jsをセットアップする
#### npxのインストール
npxを使うと、ローカルにインストールしたnpmパッケージを、npxコマンドだけで実行できるようになります。
これにより、面倒なコマンドを打たなくて済みます。
言わば"便利コマンド集"のようなもの。

普通はnpmが入れば自動的にインストールされる。
しかしnodist経由だと、npxが自動でインストールされないらしい...

そこで個別にインストールします。

```
$ npm install -g npx
```

#### プロジェクト作成
`npm create-nuxt-app <プロジェクト名>`でNuxt.jsのプロジェクトを作成可能です。
ただし、プロジェクトを作る場所はカレントディレクトリではなく、キャッシュに残っている場所になってしまいます。

しかもその場所のパスにスペースが入っていると、

```
operation not permitted, mkdir '~'
```
のエラーが出てプロジェクトを作れない。。。

そこで、エラーが出る場合は以下のコマンドでキャッシュの場所を変えます。

```
$ npm config set cache <プロジェクトを作りたいディレクトリのパス> --global
```

その上で改めて

```
$ npm create-nuxt-app <プロジェクト名>
```
を実行します。

プロジェクト作成中、UIフレームワークなど、設定を聞かれるので、適宜選択してください。

#### プロジェクトのディレクトリ構成
- layouts
    - 全ページで使用されるテンプレートファイルを規定する。ページの外観を変更するために使用される(例えばサイドバーなど)。
    - page の `layout` プロパティで変更可能
- pages
    - アプリケーションのビュー及びルーティングファイルを入れる。
    - Nuxt.js フレームワークはこのディレクトリ内のすべての .vue ファイルを読み込み、Vue Routerによって自動的にルーティングされる
        - `pages/index.vue`→ `/`
        - `pages/hoge/fuga.vue` → `/hoge/fuga`
- components
    - pages から利用するコンポーネントを入れておく
    - components から components も使ったりする
- stores
    - Vuex ストアのファイルを入れる
    - デフォルトではVuexストアは無効。このディレクトリに index.js ファイルを作成するとストアが有効になる。

### Nuxt.jsでカレンダーアプリを作ってみる
サンプルとしてカレンダーアプリ(~~と言えるかすら微妙なもの~~)を作ってみました。

#### index.vueの作成
まずはindex.vueです。pagesディレクトリ内に作ります。
コンポーネントとして作ったCalender.vueを`script`内で読み込みます。
これにより、Calender.vueの内容を、`<calender />`タグの部分にはめ込むことができます。

```html:index.vue
<template>
  <div class="container">
    <calendar />
  </div>
</template>

<script>
import Calendar from '~/components/Calendar'

export default {
  components: {
    Calendar,
  }
}
</script>

<style></style>
```

#### コンポーネントの作成(Calendar.vueとCalendarDay.vue)
Calendar.vueにカレンダーの全体像、CalendarDay.vueにカレンダーの一日の部分を入れます。<br>

```html:Calendar.vue
<template>
  <div class="calendar">
    <calendar-day v-for="day in dayList" :key="day" :day="day" />
  </div>
</template>

<script>
import CalendarDay from '~/components/CalendarDay'
export default {
  components: {
    CalendarDay,
  },
  data() {
    return {
      dayList: []
    }
  },
  created() {
    for (let i = 1; i <= 31; i++) {
      this.dayList.push(i)
    }
  }
}
</script>

<style scoped>
.calendar {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
}
</style>
```

```html:CalendarDay.vue
<template>
  <div class="calendar-day">
    {{ day }}
    <input type="text" v-model="text">
    <button @click="addSchedule({ day, text })">追加</button>
    <ul>
      <li v-for="schedule in scheduleList" :key="schedule.id">{{ schedule.text }}</li>
    </ul>
  </div>
</template>

<style scoped>
.calendar-day {
  border: solid 1px black;
  width: 150px;
  height: 150px;
}
</style>

<script>
import { mapGetters, mapMutations } from 'vuex'
export default {
  data() {
    return {
      text: '',
    }
  },
  props: {
    day: 0,
  },
  computed: {
    ...mapGetters({
      getScheduleListByDay: 'schedule/getScheduleListByDay',
    }),
    scheduleList() {
      return this.getScheduleListByDay(this.day)
    }
  },
  methods: {
    ...mapMutations({
      addSchedule: 'schedule/addSchedule'
    }),
  }
}
</script>
```

#### storeの作成(schedule.js)
storeディレクトリにschedule.jsを作成し、予定データを保存・加工します。

```javascript:schedule.js
export const state = () => ({
    scheduleList: [], // Array<{ id: Number, day: Number, text: String }>
    lastScheduleId: 0,
})

export const getters = {
    getScheduleListByDay: (state) => (day) => {
        return state.scheduleList.filter(schedule => schedule.day === day)
    },
}

export const mutations = {
    addSchedule (state, { day, text }) {
        state.scheduleList.push({
        id: ++state.lastScheduleId,
        day,
        text,
        })
    },
}
```

## おわりに
Vueを使うと、コンポーネントとしての管理がしやすいなぁと思いました。

ただ、アロー関数などJSの記法をしっかり覚えないと...