2020年9月3日
ゲーム作りで学ぶVue.js (4) Vuexで変数管理

Vuexをインストール

本章ではVuexを使って変数の定義と表示を行います。

Vuex
https://vuex.vuejs.org/ja/

インストールはこれでできます。

$ npm install vuex --save

プロジェクトのルートディレクトリで行ってください。
(package.jsonがあるディレクトリです)

Vuexに変数定義

Vuexを導入する目的はコンポーネントに依存しない共通の変数や関数をもちたいのが主です。

Vuexを使うためにsrc配下にstore.jsを作成します。

src/store.js

import Vue from "vue"
import Vuex from "vuex"

Vue.use(Vuex)

const store = new Vuex.Store({
  state: {},
  mutations: {}
})
export default store

これはお決まりの書き方で、このような意味になります。

  • VueとVuexを読み込む
  • VueでVuexを使用する
  • storeを定義
  • storeを外部で使うためにexportする

このstoreという変数が共通で使えるもので、各コンポーネントから呼び出して使っていきます。

storeに何を定義するかというと変数と関数です。
正確には4種類あります

・ステート(state)
 変数を定義する
・ゲッター(getters)
 変数をフィルタリングする
・ミューテーション(mutations)
 変数を更新する
・アクション(actions)
 外部通信を含めた複合的な処理を行う

ここではステートとミューテーションのみ扱います。

ステートには以下の変数を用意します。

  • 駒の一覧 (komaList)
  • Playerの点数 (playerPoint)
  • Comの点数 (comPoint)
  • Playerが獲得した駒 (playerKomadai)
  • Comが獲得した駒 (comKomadai)
  • Playerが選択できる駒 (playerSelectable)
  • Comが選択できる駒 (comSelectable)

これらをstateに定義します。

src/store.js

import Vue from "vue"
import Vuex from "vuex"

Vue.use(Vuex)

// 追加
const komaIndexList = ["fu", "kyosha", "keima", "gin", "kin", "kaku", "hisha"]

const store = new Vuex.Store({
  state: {  // 以下追加
    komaList: {
      fu: { point: 1, label: "歩" },
      kyosha: { point: 3, label: "香" },
      keima: { point: 4, label: "桂" },
      gin: { point: 6, label: "銀" },
      kin: { point: 7, label: "金" },
      kaku: { point: 9, label: "角" },
      hisha: { point: 10, label: "飛" }
    },
    playerPoint: 0,
    comPoint: 0,
    playerKomadai: [],
    comKomadai: [],
    playerSelectable: Object.assign([], komaIndexList),
    comSelectable: Object.assign([], komaIndexList)
  },
  mutations: {}
})
export default store

それぞれの変数の説明です。

  • 駒の一覧 (komaList)
    型:オブジェクト
    keyに駒の英語表記でポイントと表示ラベルを持ちます
    komaListは更新されません
  • PlayerとComの獲得ポイント (playerPoint, comPoint)
    型:Number
    初期値:0
    駒を獲得したらその駒の点数が加算されます
  • PlayerとComの獲得した駒 (playerKomadai, comKomadai)
    型:配列
    初期値:空
    駒を獲得したら["fu", "kyo"]のよう増えていきます
  • PlayerとComの選択できる駒 (playerSelectable, comSelectable)
    型:配列
    初期値:["fu", "kyosha", "keima", "gin", "kin", "kaku", "hisha"]
    使用した駒は減っていき最後には空になります

これでステートの変数定義は終わりです。

※Object.assignについて
Object.assign([], komaIndexList)komaIndexListをコピーするためです。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/assign

仮にこのようにするとplayerSelectableとcomSelectableは内部的には同じものになってしまい正しく動作しません。

playerSelectable: komaIndexList,
comSelectable: komaIndexList

Vuexのステートをコンポーネントで使用

store.jsのステートで定義した変数をコンポーネントで使っていきます。

そのための準備としてmain.jsstore.jsを読み込みます。

src/main.js

import Vue from "vue"
import App from "./App.vue"
import store from "./store.js"  // 追加

Vue.config.productionTip = false

new Vue({
  store,  // 追加
  render: (h) => h(App)
}).$mount("#app")

これでコンポーネントから$store.stateという名前でstore.jsstateを使うことができます。
テンプレートで使う時は$store.stateで、JSで使う時はthis.$store.stateになります。

それではkomaListを使って将棋盤の駒表示を書き換えます。

まずはCom側から。

src/components/ShogiPoker.vue

<div class="shogi-ban">
  <table>
    <tbody>
      <tr class="com-row">
        <td v-for="(koma, index) in $store.state.komaList" :key="index">
          {{ koma.label }}
        </td>
      </tr>

$store.state.komaListv-forでループすれば駒の数だけ<td>が作られます。
komaには{ point: 1, label: "歩" }が、indexにはfuが入っていきます。
タグの中身で{{}}を使えば表示することができます。

同じ要領でPlayer側も書き換えます。

src/components/ShogiPoker.vue

      <tr class="player-row">
        <td v-for="(koma, index) in $store.state.komaList" :key="index">
          {{ koma.label }}
        </td>
      </tr>
    </tbody>
  </table>
</div>   

mapStateで書き換える

$store.stateは大変便利です。
ただ少し記述が長いので短縮する方法があります。

$store.stateを短縮して使えるmapStateというヘルパーがあるのでそちらで書き換えていきます。

importでmapStateを読み込みます。

src/components/ShogiPoker.vue

<script>
import { mapState } from "vuex"  // 追加
import GameRule from "./GameRule"
import ComKomadai from "./ComKomadai"
...
</script>

computed...mapState(["komaList"]),と書けば、$store.state.komaListをそのままkomaListとして使うことができます。

src/components/ShogiPoker.vue

computed: {
  ...mapState(["komaList"]),  // 追加
  kyokumen() {
    return this.phase === 1 ? "初手" : this.phase + "手目"
  },
  displayResult() {
    return this.gameResult ? this.gameResult : this.phaseResult
  }
}

mapStateの書き方は色々あって少しややこしいのですが、以下のことを覚えておけば良いと思います。

  • mapStateはVuexのstateを短縮して使えるヘルパーである
  • $store.stateが不要になる
  • import { mapState } from "vuex"で読み込む
  • computedに使うstateを記載する
  • 記載の仕方は色々ある

ちなみに上記のcomputedは以下と同じ意味になります。

computed: {
  komaList() {
    return this.$store.state.komaList
  },
  kyokumen() {
    return this.phase === 1 ? "初手" : this.phase + "手目"
  },
  displayResult() {
    return this.gameResult ? this.gameResult : this.phaseResult
  }
}

後々...mapState(["komaList", "playerPoint", "comPoint"])のように増やしていけるので、ここでは...mapStateを使います。
...はスプレッド構文と呼ばれるものでES2015のJSではよく出てきます。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Spread_syntax

では駒一覧の表示を変えていきます。

変えるところは$store.stateを消すだけです。

Com側

src/components/ShogiPoker.vue

<div class="shogi-ban">
  <table>
    <tbody>
      <tr class="com-row">
        <td v-for="(koma, index) in komaList" :key="index">
          {{ koma.label }}
        </td>
      </tr>

Player側

src/components/ShogiPoker.vue

      <tr class="player-row">
        <td v-for="(koma, index) in komaList" :key="index">
          {{ koma.label }}
        </td>
      </tr>
    </tbody>
  </table>
</div>   

駒台にmapStateを使う

Comの駒台でもmapStateを使ってポイントを表示していきます。

src/components/ComKomadai.vue

<script>
import { mapState } from "vuex"
export default {
  computed: {
    ...mapState(["komaList", "comKomadai", "comPoint"])
  }
}
</script>  

comPointをテンプレートで使います。

src/components/ComKomadai.vue

<div class="com-point">{{ comPoint }}</div>

computedkomaListcomKomadaiは後で使用します。
上記のcomputedはこれと同義です。

computed: {
  komaList() {
    return this.$store.state.komaList
  },
  comKomadai() {
    return this.$store.state.comKomadai
  },
  comPoint() {
    return this.$store.state.comPoint
  }
}

Playerのポイントも同様にmapStateで表示します。

src/components/PlayerKomadai.vue

<script>
import { mapState } from "vuex"
export default {
  computed: {
    ...mapState(["komaList", "playerKomadai", "playerPoint"])
  }
}
</script>
src/components/PlayerKomadai.vue

<div class="player-point">{{ playerPoint }}</div>

ポイントの初期値:0が表示されていればOKです。

03-030point

ここまでのコードはこちらにあります。

本章までで変数の定義ができたので、 次章 からはこれらを更新する関数を作っていきます。

連載記事