🌴

Vuexの使い方や責務について

2021/06/25に公開

Vuexとは、状態管理のライブラリです。Vuexにおける状態(state)とは、フロントが保持するデータのことです。それらのデータは複数のコンポーネントから参照することが出来るため、propsや$emitを使ったバケツリレーを回避してくれます。

データの流れ

親から孫(孫から親)コンポーネントに、propsや$emitを使ってバケツリレーでデータをやり取りする図です。

こちらはVuexを使って、親から孫コンポーネントにデータをやり取りする図です。バケツリレーを回避しています。

そして次はVuex公式ページでよく見る図です。基本的なデータの流れは Actions → Mutations → State → Vue Components となります。しかし、Vue ComponentsはStateを直接参照せずGetterを経由することが推奨されています。

Stateを直接参照しないことに関する説明は、こちらのサイトが分かりやすいです。
https://uncle-javascript.com/vuex-getters

役割

VuexのStoreは、Actions, Mutations, Getter, Stateの4つの要素から構成されています。

State

  • データの入れ物です
  • 各種APIレスポンスをそのまま保持します
  • ユーザーのログイン情報など、複数コンポーネントを跨ぎたい情報を管理します

Actions

  • Mutationsを介して、Stateを更新するメソッドです
  • 非同期処理でなければなりません

Mutations

  • Stateを更新するメソッドです
  • 同期処理でなければなりません

Getters

  • Stateの内容から算出される値です
  • Componentにデータを加工して提供します(Viewに表示させる)

それぞれ役割については以上です。

Stateを更新して良いのはMutationsのみで、Componentにデータを渡すのはGettersのみと制約がはっきりしています。

GettersをViewに依存させない

先ほどGettersの役割は、Componentにデータを加工して提供すると書きました。しかし、表示用の加工処理をGettersに任せてしまうと、Storeの処理が肥大化し管理が難しくなります。

そのため、StoreからGettersで取得した値をComputedなどで処理をして表示するようにします。

Vuexの責務

Vuexを使うとバケツリレーの回避ができますが、目的をバケツリレーの回避(propsや$emitのショートカット)にするのはアンチパターンと言われています。

アンチパターンの理由は、どこで何が起きているのかを追うのが難しくなり、予期せぬ所からデータが更新されてしまったり、テストが難しくなったりと不具合を起こす要因になるためです。

Vuexに入れるべきデータは慎重に検討するべきです。対策として例えば、Atomic Designを導入している場合は、PagesやTemplatesなどの大きな単位に限ったり、コンポーネントに閉じれるデータは、そのコンポーネント内で管理するなどです。

Vuexを使う場合は、propsや$emitによるバケツリレーの回避を目的とするのではなく、データの流れを一方向にし、グローバルなデータを安全に一元管理にすることに意識を向ける事で、Vuexがよりセーフティになります。

サンプルコード

次のコードはstoreを直接参照していますが、とりあえず動かしてみたい人向けの簡単なサンプルコードです。

1.storeを使ってHello Vuex!!!を出力

% vue init webpack my-projectで作成したプロジェクトにVuexをインストールします。そしてstoreを使って、画面上に「Hello Vuex」を表示します。

App.vue

<template>
  <div id="app">
    <h1>{{ $store.state.message }}</h1>
  </div>
</template>

main.js

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

Vue.use(Vuex)

const store = new Vuex.Store({
  state: {
    message: 'Hello Vuex!!!',
  },
})

new Vue({
  el: '#app',
  template: '<App/>',
  components: { App },
  store,
})

2.Mutationsを使ってカウントアップ

次はMutationsを使い、UPボタンをクリックで数がカウントアップするコードです。

<template>
  <div id="app">
    <p><button @click="increment">UP</button>
    <h1>Count:{{ this.$store.state.count }}</h1>
  </div>
</template>

<script>
export default {
  name: 'app',
  methods: {
    increment() {
      this.$store.commit('increment')
    }
  }
}
</script>
const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment(state) {
      state.count++
    }
  }
})

3.2件ずつカウントアップ

Commitには値を渡すことができ、これをpayloadと呼びます。先ほどはボタンクリックで1つ値が増えましたが、一度に2増ずつやしたい場合は、数値の2をpayloadに設定します。そうすることで、VuexのMutationsに設定した値を渡せます。

methods: {
  increment() {
    this.$store.commit('increment', 2)
  }
}
mutations: {
  increment(state, payload) {
    state.count = state.count + payload
  }
}

4.Actionsを使って、カウントアップ

Vuexでは通常のvue.jsのMethodsに対応するActionsがあります。ActionsはStateを直接変更するのではなく、Mutationsを経由してStateを更新します。

methods: {
  increment() {
    this.$store.dispatch('incrementActions')
  }
}
const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment(state) {
      state.count++
    }
  },
  actions: {
    incrementActions(context) {
      context.commit('increment')
    }
  }
})

参考

https://vuex.vuejs.org/ja/
https://uncle-javascript.com/vuex-getters
https://aloerina01.github.io/blog/2019-09-25-1

Discussion