0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Vuex ハンズオン

Posted at

前提

端末: MacBook Pro
OS: macOS Catalina 10.15.7
Node.js: v12.18.3
Vue.js: v2.6.11
Vue-CLI: v4.5.13

Node.js, yarn, Vue 既にインストール済み
Vue-CLI を利用して、プロジェクトを作成済み

Vuex とは

Vue.js アプリケーション用の状態管理ライブラリのことを指す
データの状態管理を一元化して開発効率を上げることを目的として、
アプリケーションで扱うデータセットを Store と呼ばれる領域で一元管理することで、
各コンポーネントは Store にアクセスすれば常に共通の値を参照することができるようになる

どんな時に使っているのか

中規模から大規模のシステム、 SPA を構築時に使用することを推奨
複数のページにおいて、共通利用する情報を保持したい際に使用する
共通利用する値なのにも関わらず、コンポーネントが親、子、孫、ひ孫・・という関係になった際に、
必要な情報を全て props でバケツリレーするのは非常に面倒であり、バグの元となる
上記の状態になった際に vuex を使用して、どのコンポネからも取得、変更をすることを可能にする

セットアップ

yarn でインストール
(Vue-CLI でプロジェクト作成した場合だと、既にインストール済みの可能性あり)

$ yarn add vuex

package.json に定義されているか確認
※npm, yarn で何かインストールした際は、逐一確認する癖をつけておくと細かいミスが減る

package.json
{
  "dependencies": {
    "core-js": "^3.6.5",
    "vue": "^2.6.11",
    "vuex": "^3.6.2"
  },
}

Vuex におけるストア

ストアは主にアプリケーションの状態を保持する役割を担う為、 Vuex の根幹とある
ストアを作成するにあたり、定義できる項目は以下の 4 つ

項目名 概念
state ストアで管理するデータ定義
getters state 内のデータから計算された値を返却する
mutations state 内のデータを更新する
actions mutations の操作を各コンポーネントから呼び出す

ストアの state を使用してみる

store.js を /src 配下に作成する

store.js
import Vue from 'vue'
import Vuex from 'vuex'

// モジュールとして利用する場合は Vue.use() によって明示的に導入する必要がある
Vue.use(Vuex)

// Vuex アプリの中心にあるストアの作成
export default new Vuex.Store({
  state: {
    count: 0
  },
})

main.js を修正する

main.js
import Vue from 'vue'
import App from './App.vue'
import store from './store'

Vue.config.productionTip = false

new Vue({
  // Vue インスタンス生成時にストア情報を定義する
  store,
  render: h => h(App),
}).$mount('#app')

App.vue を修正する

App.vue
<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png">
    <h1>Hello Vuex Hands on!</h1>

    <div>
      <h2>STORE</h2>
      {{ showCount }}
    </div>
  </div>
</template>

<script>
export default {
  name: 'App',

  computed: {
    // ストアから値を取り出すには computed で返却することが推奨されている
    // (ストア自体はリアクティブのため値が変更時に更新される目的)
    showCount() {
      // state の値は $store から取得可能
      return this.$store.state.count
    }
  },
}
</script>

下記のコマンドを叩いてローカルサーバーを起動

$ yarn serve

ストアに定義した count の値が表示できていることが確認できる

vuejs_hands_on_vuex_store.png

Vue.js のデベロッパーツールにも Vuex のタブが存在し、
ストアに count が定義されていることが確認できる

vuejs_hands_on_vuex_devtool.png.png

複数の steta の値を使用する際に、全て computed に定義するのは冗長のため、
mapState ヘルパーを使用することを推奨している

App.vue を修正する

App.vue
<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png">
    <h1>Hello Vuex Hands on!</h1>

    <div>
      <h2>STORE</h2>
      {{ count }}
    </div>
  </div>
</template>

<script>
import { mapState } from 'vuex'

export default {
  name: 'App',

  computed: {
    // state のプロパティ名を指定する
    ...mapState['count'],
  },
 }
 </script>

ストアの getters を使用してみる

ストアに getters を定義しておくことで、 Vuex 内にて算出プロパティー(computed)として使用することが可能

store.js を修正する

store.js
// Vuex アプリの中心にあるストアの作成
export default new Vuex.Store({
  // 定義したデータは、どのコンポーネントからも使用することが可能
  state: {
    count: 2
  },

  // computed の様に state の値を算出することが可能
  getters: {
    doubleCount: state => state.count * 2,
    tripleCount: state => state.count * 3
  }
})

App.vue を修正する

App.vue
<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png">
    <h1>Hello Vuex Hands on!</h1>

    <div>
      <h2>STORE</h2>
      STATE: {{ count }}<br>
      GETTER: {{ doubleCount }}, {{ tripleCount }}
    </div>
  </div>
</template>

<script>
import { mapState, mapGetters } from 'vuex'

export default {
  name: 'App',

  computed: {
    ...mapState(['count']),
    ...mapGetters(['doubleCount', 'tripleCount']),
  },
}
</script>

ストアに定義した getters の値が表示できていることが確認できる

vuejs_hands_on_vuex_getters.png

デベロッパーツールの Vuex のタブにも、
getters の値が定義されていることが確認できる

vuejs_hands_on_vuex_getters_devtool.png

ストアの mutations を使用してみる

ストアの state の値を変更、更新する際に、下記のように直接更新・削除を行なうことを禁止されており、
mutations のみが行える原則

created() {
  // やろうと思えばできなくはない(エラー等も発生しない)が非推奨
  this.$store.state.count = 100 
}

store.js を修正する

store.js
// Vuex アプリの中心にあるストアの作成
export default new Vuex.Store({
  // 定義したデータは、どのコンポーネントからも使用することが可能
  state: {
    count: 2
  },

  // computed の様に state の値を算出することが可能
  getters: {
    doubleCount: state => state.count * 2,
    tripleCount: state => state.count * 3
  },

  // ストアの state の値を変更、更新する場合は mutations を使用する
  // mutations 内で行う処理は全て同期的にする必要がある(非同期の場合は actions を使用する)
  mutations: {
    // 呼び出し元にて store.commit の第二引数に値を定義すると、ハンドラーの第二引数に渡される
    // 上記の値のことをペイロードと呼ぶ仕様。ペイロードはオブジェクトで渡されることが推奨されている
    update(state, payload) {
      state.count = payload.value
    }
  }
})

App.vue を修正する

App.vue
<script>
import { mapState, mapGetters, mapMutations } from 'vuex'

export default {
  name: 'App',

  created() {
    this.update({
      value: 30
    })
  },

  computed: {
    ...mapState(['count']),
    ...mapGetters(['doubleCount', 'tripleCount']),
  },

  methods: {
    ...mapMutations(['update']),
  }
}
</script>

ストアに定義した stete, getters の値が更新できていることが確認できる

vuejs_hands_on_vuex_mutations.png

デベロッパーツールの Vuex のタブにも、
mutations の値が定義、呼ばれていることが確認できる

vuejs_hands_on_vuex_mutations_devtool.png

ストアの actions を使用してみる

actions は状態を変更するのではなく、 mutations をコミットする役割
外部 API とのやり取りや、非同期処理を含む際は actions に定義する

store.js を修正する

store.js
// Vuex アプリの中心にあるストアの作成
export default new Vuex.Store({
  // 定義したデータは、どのコンポーネントからも使用することが可能
  state: {
    count: 2
  },

  // computed の様に state の値を算出することが可能
  getters: {
    doubleCount: state => state.count * 2,
    tripleCount: state => state.count * 3
  },

  actions: {
    update(context) {
      // actions ハンドラにおける context.commit を呼び出すことで mutations にコミットすることが可能
      context.commit('update', { value: 30 })
    }
  },

  // ストアの state の値を変更、更新する場合は mutations を使用する
  // mutations ないで行う処理は全て同期的にする必要がある(非同期の場合は actions を使用する)
  mutations: {
    // 呼び出し元にて store.commit の第二引数に値を定義すると、ハンドラーの第二引数に渡される
    // 上記の値のことをペイロードと呼ぶ仕様。ペイロードはオブジェクトで渡されることが推奨されている
    update(state, payload) {
      state.count = payload.value
    }
  }
})

App.vue を修正する

App.vue
<script>
import { mapState, mapGetters, mapActions } from 'vuex'

export default {
  name: 'App',

  created() {
    this.update()
  },

  computed: {
    ...mapState(['count']),
    ...mapGetters(['doubleCount', 'tripleCount']),
  },

  methods: {
    ...mapActions(['update'])
  }
}
</script>

ストアに定義した stete, getters の値が actions 経由で更新できていることが確認できる

vuejs_hands_on_vuex_actions.png

デベロッパーツールの Vuex のタブにも、
mutations の値が actions 経由で定義、呼ばれていることが確認できる

vuejs_hands_on_vuex_actions_devtool.png

コンポーネントから、 actions に引数を渡したい場合は、第二引数の payload を定義する
store.js を修正する

store.js
actions: {
  // コンポーネントから引数を受け取る場合は、第二引数の payload にて取得可能
  update(context, payload) {
    // actions ハンドラにおける context.commit を呼び出すことで mutations にコミットすることが可能
    context.commit('update', { value: payload.value })
  }
},

// ストアの state の値を変更、更新する場合は mutations を使用する
// mutations ないで行う処理は全て同期的にする必要がある(非同期の場合は actions を使用する)
mutations: {
  // 呼び出し元にて store.commit の第二引数に値を定義すると、ハンドラーの第二引数に渡される
  // 上記の値のことをペイロードと呼ぶ仕様。ペイロードはオブジェクトで渡されることが推奨されている
  update(state, payload) {
    state.count = payload.value
  }
}

App.vue を修正する

App.vue
<script>
export default {
  name: 'App',

  created() {
    this.update({
      value: 30
    })
  },

  computed: {
    ...mapState(['count']),
    ...mapGetters(['doubleCount', 'tripleCount']),
  },

  methods: {
    ...mapActions(['update'])
  }
}
</script>

コンポーネントから actions へ渡した引数で更新できていることが確認できる

vuejs_hands_on_vuex_actions.png

ストアをモジュールに分割する

アプリケーションが大きくなるにつれ、ストアで管理したいデータも増えていく
そういった際に、ストアをモジュールで分割することが可能(koto や luxa でも対応済み)

今まで使用していた store.js から下記のソースを抜き出し、 count.js を作成する
(store 専用のディレクトリを作成するのが好ましいが、今回は /src 配下に作成する)

count.js
export default {
  // 下記の定義がないと、各コンポーネントから参照する際にエラーが発生する
  namespaced: true,

  // 定義したデータは、どのコンポーネントからも使用することが可能
  state: {
    count: 2
  },

  // computed の様に state の値を算出することが可能
  getters: {
    doubleCount: state => state.count * 2,
    tripleCount: state => state.count * 3
  },

  actions: {
    update(context) {
      // actions ハンドラにおける context.commit を呼び出すことで mutations にコミットすることが可能
      context.commit('update', { value: 30 })
    }
  },

  // ストアの state の値を変更、更新する場合は mutations を使用する
  // mutations ないで行う処理は全て同期的にする必要がある(非同期の場合は actions を使用する)
  mutations: {
    // 呼び出し元にて store.commit の第二引数に値を定義すると、ハンドラーの第二引数に渡される
    // 上記の値のことをペイロードと呼ぶ仕様。ペイロードはオブジェクトで渡されることが推奨されている
    update(state, payload) {
      state.count = payload.value
    }
  }
}

store.js を修正する

store.js
import Vue from 'vue'
import Vuex from 'vuex'
import count from './count';

// モジュールとして利用する場合は Vue.use() によって明示的に導入する必要がある
Vue.use(Vuex)

// Vuex アプリの中心にあるストアの作成
export default new Vuex.Store({
  modules: {
    count
  }
})

App.vue を修正する

App.vue
<script>
import { mapState, mapGetters, mapActions } from 'vuex'

export default {
  name: 'App',

  created() {
    this.update()
  },

  computed: {
    // 第一引数に読み込むモジュールを定義する
    ...mapState('count', ['count']),
    ...mapGetters('count', ['doubleCount', 'tripleCount']),
  },

  methods: {
    ...mapActions('count', ['update'])
  }
}
</script>

問題なく、 ストアのデータを更新、取得できていることが確認できる

vuejs_hands_on_vuex_module.png

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?