公式リリースされた Vuex 4 を使ってVue Composition API で状態管理する

2 min read読了の目安(約2500字

先日、Vue Composition APIに完全対応したVuexが公式に発表されました。この記事では、その導入方法や、Vue Composition APIでの使用法を中心にまとめていきたいと思います。

https://github.com/vuejs/vuex/releases/tag/v4.0.0

インストール

現在(2020/2/3)において、既存のリポジトリにVuexを導入するには、

# npm の場合
npm install vuex@next --save 

# yarn の場合
yarn add vuex@next --save

で、インストールします。

続いて、/store/index.tsで、

import { createStore } from 'vuex'

export const store = createStore({
  state () {
    return {
      count: 0
    }
  },
  mutations: {
    increment (state) {
      state.count++
    }
  }
})

とします。

main.ts にて、

import { createApp } from 'vue'
import App from './App.vue'
import router from './router';
import {store} from "./store";

const app = createApp(App)
  .use(store)
  .use(router)

router.isReady().then(() => {
  app.mount('#app');
});

これにより、 Vuexの初期設定は完了です。

Vue Composition API 上で Vuex を呼び出す。

Vue 3より公式に導入されたVue Composition APIでVuexを実際に使用していきます。setup() 内ではthis.$store を使用できません。代わりに useStore() を使用してcomponent内でGlobal Stateを扱います。

Vuexにおいて、

  • store, getterを使うとき → computed() を利用する
  • mutation, actionを使うとき → methods を利用する
// 省略

<script>
import {computed} from "vue"
import {useStore} from "vuex"

export default {
    setup(){
        const store = useStore()
        return {
            // state を呼び出す場合
            count: computed(()=>store.state.count),

            // getters を呼び出す場合
            double: coumputed(()=>store.getters.double),
            
            // mutation を呼び出す場合
            increment:() =>store.commit("increment"),

            // action を呼び出す場合
            asyncIncrement:() => store.dispatch("asyncIncrement")
        }
    }
}
</script>

以上のように、setup() 内でVuexを使用します。

TypeScript Support

Vue 3.xまでは、 Vuexの型を定義する上で、公式が GetterTree, ActionTree, MutationTree などを提供しています。

しかし、

  • Getterの戻り値の型が any になってしまう
  • ActionとMutationのpayloadが any になる
  • Editor自体のTypeScriptの型補完がうまくいかない

などの問題からTypeScriptとの相性がよくありませんでした。

Vue 4.0から、InjectionKey を使用して型を定義します。
先ほどの /store/index.ts に型を定義します。

import {InjectionKey} from "vue"
import { createStore, Store, useStore as baseUseStore } from 'vuex'

export interface GlobalState{
    count:number
}

export const StateKey: InjectionKey<Store<GlobalState>> = Symbol()

export const store = createStore({
  state () {
    return {
      count: 0
    }
  }
})

// useState を呼び出す度、 StateKey で型を定義するのを避けるために、ここであらかじめ定義する
export function useStore(){
    return baseUseStore(StateKey)
}

以上で、useStore() に型を付与したものをComponent内で使用できます。

参照