Vueを学ぶうえで、スロットゲームを作る事で知識がとても深まった話

## はじめに
以下本（Vue.js入門 基礎から実践アプリケーション開発まで）の8章以降の「中規模・大規模向けのアプリケーション開発」で挫折してしまったので、「スロットゲーム」を作り、知識を深める事にした。（ただ、誤解のないように言うと、この本自体は素晴らしい本で、私がこの本を手にするには、レベルが低すぎたという話:sob:）

![Vue_js入門.png](https://qiita-image-store.s3.amazonaws.com/0/241956/5182dc9a-4083-a6db-93de-6267976a008d.png)

この試みが、思っていた以上に良かったので記事にしたいと思う。
Vueに苦手意識や本を読んでもイマイチよくわからなかった人は、是非「スロットゲーム」作りにチャレンジしてみて欲しい。

ちなみに、私が作った「スロットゲーム」は以下技術を使用している。

- コンポーネントの再利用
- コンポーネント間の連携（Vuex）
- ルーティング（Vue-Router）

## 作ったもの
![slot-game.gif](https://qiita-image-store.s3.amazonaws.com/0/241956/ba1c7dd2-d16c-5d85-2dcc-4cb1392dcf45.gif)

以下、設計と実装について、個人的な感想も含めて書いていく。
あくまで個人的な感想であること、今回作成したスロットゲームは、あくまでJavaScriptとVueを学びたての人が作ったという温かい目線で見て頂きたい:bow_tone1:

## 設計（１）
![slot-des1.png](https://qiita-image-store.s3.amazonaws.com/0/241956/ff247326-d5a2-9adc-533b-c51378cd063e.png)
「スロットゲーム」を作ってみてわかったのだが、Vueは設計がとても大事だ。
何をコンポーネントにするのか、コンポーネントのInputとOutputを何にするのか、設計段階で決めておかないと再利用性のないコンポーネントになってしまうので、非常に重要だと思った。

今回作成する「スロットゲーム」は、上記５つのコンポーネントに分けることにした。
②は、コンポーネントを再利用する。

※モーダル画面もコンポーネント化して実装しているが、ここでは割愛する。

## 設計（２）
![slot-des2.png](https://qiita-image-store.s3.amazonaws.com/0/241956/8947aa88-99ea-74b7-d8d8-05b51dac4e95.png)

①のコンポーネントを親とし、②と③のコンポーネントを子とする。
②と③のコンポーネントは再利用できるように設計した。その際、親のコンポーネントの仕様に引きづられないように設計するのがポイントだと感じた。また、Vuexのstateから値を直接取得するのも止めた方が良いと思った。Vuexのstateに値が無いと使えないようなコンポーネントは再利用性が無いと思った。

なので、①の親コンポーネントでVuesのstateへの保存・取得を行い、それを子コンポーネントへ渡すような設計にした。

## 設計（３）
![slot-des3.png](https://qiita-image-store.s3.amazonaws.com/0/241956/2f835ff4-e9f3-7350-22b3-85aee674b8b3.png)

④と⑤のコンポーネントは、個人的にVue-Routerを使ってみたかったという思いから作成した。
とりあえず使いそうな値はVuexのstateに保存しておくと、どのページでも値を取得できるのでとても便利だと感じた。

## 実装
全てのソースコードを記載すると長くなるので、Gameコンポーネントだけ記載する。
興味がある方は以下を参照して欲しい。

https://github.com/Kodak4400/Vuejs-studing/tree/master/04-slot-app

```Game.vue
<template>
  <div id="game">
    <Start v-on:slot-start="onStart"></Start>
    <SlotBase v-on:slot-selected-value="inputResult" v-bind:stateflg="this.$store.getters.getStateflg"></SlotBase>
    <SlotBase v-on:slot-selected-value="inputResult" v-bind:stateflg="this.$store.getters.getStateflg"></SlotBase>
    <SlotBase v-on:slot-selected-value="inputResult" v-bind:stateflg="this.$store.getters.getStateflg"></SlotBase>
    <Modal v-if="this.$store.getters.getModalflg" v-bind:result="result" v-on:close="offModal"></Modal>
  </div>
</template>

<script>
import Start from '@/components/Start.vue'
import SlotBase from '@/components/SlotBase.vue'
import Modal from '@/components/Modal.vue'

export default {
  name: 'game',

  // SlotBaseの戻り値を一時的に保持するために使用する
  data () {
    return {
      result: []
    }
  },

  components: {
    Start,
    SlotBase,
    Modal
  },

  watch: {
    result () {
      // SlotBaseコンポーネントの数を知る
      let slots = document.querySelectorAll('[id^=slot-value-]')
      // 全てのSlotBaseコンポーネントが動作完了時に実行する
      if (this.result.length === slots.length) {
        // resultの値をスロット順に入替
        let i = 0
        for (let slotobj of slots) {
          this.result[i] = slotobj.value
          i++
        }
        // モーダル表示
        this.onModal()
        this.$store.commit('addResult', {
          result: this.result
        })
        this.offStart()
      }
    }
  },

  created () {
    this.resultMessage = ''
    this.offStart()
    this.result = []
  },

  methods: {
    inputResult (num) {
      this.result.push(num)
    },
    onStart () {
      this.$store.commit('chgStateflg', {
        stateflg: true
      })
    },
    offStart () {
      this.$store.commit('chgStateflg', {
        stateflg: false
      })
    },
    onModal () {
      this.$store.commit('chgModalflg', {
        modalflg: true
      })
    },
    offModal () {
      this.$store.commit('chgModalflg', {
        modalflg: false
      })
      // 一時的に保持している値をリセット
      this.result = []
    }
  }
}
</script>
```

私が最も悩んだのは、②のコンポーネントを再利用した時のスロットの数の把握とスロットを止めた時の値の順番だ。
watchを見て欲しいのだが、スロットの数については、document.querySelectorAllで取得する事ができた。
しかし、スロットを止めた時の値の順番については、そのままだとスロットを止めた順に値が配列に格納されてしまうので、わざわざHTMLのスロット順に値の再取得を行っている。（苦肉の策）
コンポーネントを再利用した時の処理順を意識した実装はなかなか難しいかもしれない:disappointed_relieved:
（私の技術不足なのかもしれませんが・・・orz）

## まとめ
- 単純な設計だが、とりあえず「えいやっ」で実装から始めてしてしまうと、再利用性のないコンポーネントになる可能性が高く、Vueで何か作るなら設計が大事という事を学んだ。
- 子コンポーネントからVuexのstateから値を取得すると再利用性のないコンポーネントになる可能性が高いと思った。（Vuexのstateは親側で取得し、子に渡すのが良いと思った）
- Vuexのstateが便利すぎて感動した。
- 設計さえできてしまえば、実装は比較的楽。個人的にピタゴラスイッチ的に処理が動いていくの見て、とても楽しく実装できた。

（追記）
この記事を会社で有識者に見てもらったところ、コンポーネント再利用時のidセレクタの重複はHTML的にNGとのこと。そのため、idセレクタがHTML内で重複しないような工夫を施した。
こういったところもVueを使う上で大変勉強になった。

最後に、Vueを学ぶにあたって「スロットゲーム」作りおすすめです！！:grinning: