Vuexの基本概念と使い方

Vuex公式ドキュメント

  1. Vuexの理解 ============

1.1 概要

  • Vueアプリケーションにおける複数コンポーネント間で共有する状態集中管理する仕組み
  • state: アプリケーションのデータソース(データ)
  • view: stateを宣言的にビューレイヤーにマッピング
  • actions: ビューでのユーザー操作による状態変更を処理(複数の状態更新メソッドを含む)

1.2 複数コンポーネント間の状態共有問題の解決

  • 同じ状態を複数のビューが依存している
  • 異なるビューからの操作で同じ状態を変更したい
  • 従来の解決方法
  1. 親コンポーネントにデータとその操作メソッドを定義
  2. 子コンポーネントにデータと操作メソッドを渡す(複数階層の渡し方が必要)
  • Vuexはこの問題を解決するためのツール
  1. storeファイル構成 =============
  • index.js:Vuexのメイン管理オブジェクトstoreのモジュール
  • state.js:アプリケーションのデータソースとなる状態オブジェクト
  • mutations.js:直接stateを更新するメソッド群を保持
  • actions.js:commit経由でmutationを呼び出し、stateを更新するメソッド群
  • getters.js:stateに基づく算出プロパティを保持
  • mutation-types.js:mutationのタイプ名を定数として管理(任意)

2.1 状態管理の基本構造

  • state:アプリケーションのデータソース
  • view:stateを宣言的に表示
  • actions:ビューからのイベントで状態を変更

2.1.1 state

Vuexが管理する状態オブジェクトで、唯一のデータソース

// アプリケーションのデータソース
const state = {
    userName: 'Alice',
    items: [
        {
            id: 1,
            name: "Apple"
        },
        {
            id: 2,
            name: "Banana"
        }
    ]
}

2.1.2 mutations

  • **stateを直接変更するメソッド(コールバック)**を持つオブジェクト
  • 呼び出し方法:actionからcommit('mutation名')で呼び出す
  • 同期処理のみ許可、非同期リクエストは不可
const mutations = {
    changeName (state, payload) {
        // stateの特定プロパティを更新
        state.userName = payload.newName
    }
}

2.1.3 actions

  • イベントハンドラを含むオブジェクト、非同期処理も可能
  • 原理:commit()によりmutationを実行してstateを更新
  • 呼び出し方法:通常はコンポーネント内からthis.$store.dispatch('updateUser', newName)
const actions = {
    updateUser ({commit, state}, newName) {
        commit('changeName', {newName})
    }
}
actionsとmutationsの違い
  • Actionは直接stateを変更せず、Mutationを通じて変更する
  • Actionは非同期処理を含められる
  • Mutationは同期のみ、Actionは非同期も可能

2.1.4 getters

  • **計算されたプロパティ(getter)**を持つオブジェクト
  • 呼び出し方法:通常はコンポーネント内でthis.$store.getters.AAA
const getters = {
    AAA (state) { return 'Hello' }
}
  1. Vuexの利用手順 ===========

インストール

npm install --save-dev vuex

3.1 index.jsの作成

Vuexのメイン管理オブジェクトの定義

import Vue from 'vue'
import Vuex from 'vuex'

import state from './state'
import mutations from './mutations'
import actions from './actions'
import getters from './getters'

import userModule from './user' // ユーザーモジュール

Vue.use(Vuex)

export default new Vuex.Store({
  state,
  mutations,
  actions,
  getters,
  modules: {
    userModule, // ユーザーモジュール
  }
})

3.2 state.jsの作成

export default {
  lat: 35.6895, // 緯度
  lng: 139.6917, // 経度
  location: {}, // 位置情報
  categories: [], // カテゴリリスト
  shops: [], // 店舗リスト
}

3.3 mutations.jsの作成

stateを直接更新するメソッド群

引数はオブジェクト形式

非同期処理は不可

import {
  SET_LOCATION
} from './mutation-types'

export default {
  // 位置情報を設定
  [SET_LOCATION](state, payload) {
    state.location = payload.location
  }
}

3.4 actions.jsの作成

Mutationをcommitしてstateを更新するメソッド群

import {
  SET_LOCATION
} from './mutation-types'

import {
  fetchLocation
} from '../api/location'

export default {
  // 位置情報を取得
  async fetchLocationInfo ({commit, state}, params) {
    const response = await fetchLocation(state.lat, state.lng)
    const location = response.data
    
    commit(SET_LOCATION, {location})
  }
}

3.5 getters.jsの作成

stateに基づく計算プロパティ

export default {
  // カート内の商品総数
  totalItems(state) {
    return state.cart.reduce((total, item) => {
      return total + item.quantity
    }, 0)
  },
  
  // 合計金額
  totalPrice(state) {
    return state.cart.reduce((total, item) => {
      return total + item.price * item.quantity
    }, 0)
  }
}

3.6 mutation-types.jsの作成(任意)

mutationのタイプ定数を定義

export const INCREASE_COUNT = 'increase_count'
export const DECREASE_COUNT = 'decrease_count'
export const CLEAR_CART = 'clear_cart'

3.7 storeの設定

  1. 通常はmain.jsで行う
  2. storeオブジェクトが登録されると、各コンポーネントに$storeプロパティが追加される。主なプロパティは:
state: 登録されたstateオブジェクト

getters: 登録されたgettersオブジェクト

メソッド: dispatch(action名, データ): actionを実行

main.jsでの設定例:

import store from './store'

new Vue({
  el: '#app',
  render: h => h(App),
  router,
  
  store // storeオブジェクトの登録
})

3.8 コンポーネントでの使用

3.8.1 $storeオブジェクトの利用

メモ:module名は'users'とする

// actionsのメソッド呼び出し
this.$store.dispatch('addItem')
// モジュール管理の場合
this.$store.dispatch('users/addItem')

// stateの取得
this.$store.state.counter
// モジュール管理の場合
this.$store.state.users.counter

// gettersの取得
this.$store.getters.isEven
// モジュール管理の場合
this.$store.getters["users/isEven"]

3.8.2 map関数を利用したマッピング

インポート
import {mapState, mapGetters, mapActions} from 'vuex'
メソッドのマッピング
methods: {
  ...mapActions(['fetchLocation', 'fetchCategories']),
  
  // モジュール管理の場合
  ...mapActions('users', ['fetchLocation', 'fetchCategories']),
}
stateのマッピング(computedで使用)
computed: {
  ...mapState(['counter', 'value']),
  
  // モジュール管理の場合
  ...mapState('users', {
    counter: state => state.counter,
    value: state => state.value,
  }),
}
gettersのマッピング(computedで使用)
computed: {
  ...mapGetters(['isEven']),
  
  // モジュール管理の場合
  ...mapGetters('users', ['isEven'])
}

3.8.3 actionsの呼び出し

computedでmapStateまたはmapGettersを定義

// ...mapState(['location', 'categories']) はthis.$store.state.locationと同等
// ...mapGetters(['isEven']) はthis.$store.getters.isEvenと同等

呼び出し

mounted() {
  // 位置情報の取得
  this.fetchLocation()
  
  // カテゴリ一覧の取得
  this.fetchCategories()
}

タグ: Vuex vue.js state-management javascript front-end

8月5日 10:23 投稿