はじめに
Vue 3のComposition APIに対応した、Vuex 4が2月3日にリリースされました。
Vuex 4はTypeScriptをサポートしているため、TypeScriptのサポートが強化された、Vue 3のComposition APIと組み合わせることで、よりタイプセーフなアプリケーションを開発できるようになりました。
そこで、今回は、Vuex 4をComposition API上で、どのように使うことができるか紹介します(Composition APIについては、こちらをご覧ください)
インストール
新規でプロジェクトを作成する場合は、最新のVue CLIでプロジェクトを作成すると、Vuex 4がインストールされるようになっています。
もし、既存のプロジェクトにVuex 4を導入する場合は、以下の通りインストールします。
1 2 3 | npm install vuex@next --save # or yarn add vuex@next --save |
Storeの設定
StoreをTypeScriptで実装する場合は、以下のように、
main.ts
の
createApp()
メソッドで
store
と
key
を渡します。
1 2 3 4 5 6 7 8 9 | import { createApp } from "vue"; import App from "./App.vue"; import router from "./router"; import { store, key } from "./store"; createApp(App) .use(store, key) // storeとInjectionKeyを渡す .use(router) .mount("#app"); |
Storeの作成
まず初めに、Storeの作成について見ていきます。複数の本(名前と著者)のデータを保存するStoreをTypeScriptで実装した例がこちらです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | import { InjectionKey } from 'vue' import { createStore, useStore as baseUseStore, Store } from 'vuex' export interface Book { name: string, author: string } // ストアの型を定義する export interface State { books: Book[] } // InjectionKeyを定義する export const key: InjectionKey<Store<State>> = Symbol() // Storeを作成する export const store = createStore<State>({ state: { books: [{name: 'hoge1', author: 'fuga'}, {name: 'hoge2', author: 'fuga'}] }, getters: { getFirstBook: (state) => { return state.books ? state.books[0] : {} as Book } }, actions: { add ({ commit, state }, book: Book) { commit('add', { book: book }) } }, mutations: { add (state, { book }) { state.books.push(book) } } }) // 独自のuserStoreメソッドを定義する export function useStore () { // InjectionKeyをuserStoreメソッドに渡す return baseUseStore(key) } |
まず、注目する点は、Storeの作成をcreateStoreメソッドで行うようになった点です。定義したStoreインタフェースを型として渡してます。引数にはStateのほか、
Getter
や
Action
などを渡しています。
1 2 3 4 5 6 7 8 9 10 | export const store = createStore<State>({ state: { books: [{name: 'hoge1', author: 'fuga'}, {name: 'hoge2', author: 'fuga'}] }, getters: { getFirstBook: (state) => { return state.books ? state.books[0] : {} as Book } }, // 省略 |
次に注目する点は、
InjectionKey
です。StoreをTypeScriptで定義するには、これが必要になります。Storeの型を渡して、
InjectionKey
を生成します。
1 2 | // InjectionKeyを定義する export const key: InjectionKey<Store<State>> = Symbol() |
最後に注目する点は、独自のuseStoreメソッドを定義している箇所です。Componentの
Setup ()
メソッド内からStoreにアクセスするには、このuseStoreメソッドを使う必要があります。
加えて、型情報のついたStoreインスタンスを取得するためには、
useStore()
メソッドに、先ほど生成したInjectionKeyを渡す必要があります。
StoreをComponentから使用する
上記のStoreの使用例はこちらです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | <template> <div class="home"> <div> <input v-model="book.name" /> <input v-model="book.author" /> <button v-on:click="add">追加</button> </div> <table> <thead> <tr> <th>著者</th> <th>名前</th> </tr> </thead> <tbody> <tr v-for="book in books" :key="book.name"> <td>{{ book.author }}</td> <td>{{ book.name }}</td> </tr> </tbody> </table> </div> </template> <script lang="ts"> import { computed, reactive } from "vue" import { useStore, Book } from '@/store' export default { setup () { // 追加する本の入力蘭のv-model const book = reactive({ name: '', author: '' }) // Storeを取得する const store = useStore() return { book, // v-model books: computed(() => store.state.books), // 本の一覧を返す add: () => store.dispatch('add', { name: book.name, author: book.author } as Book) // 本を追加する } } } </script> |
先ほど述べた通り、Componentの
Setup()
メソッド内でStoreにアクセスするためには、
useStore()
メソッドでStoreインスタンスを取得する必要があります。
1 2 | // Storeを取得する const store = useStore() |
Storeのパラメータをリアクティブにアクセスしたい場合は、
computed()
メソッドを定義します。
また、
Mutation
、もしくは、
Action
にアクセスする場合は、
methos
として定義します。
1 2 3 4 5 | return { book, // v-model books: computed(() => store.state.books), // 本の一覧を返す add: () => store.dispatch('add', { name: book.name, author: book.author } as Book) // 本を追加する } |
$storeプロパティに型をつける
$store
プロパティに型をつけるには、型定義ファイルを作る必要があります(型定義ファイルの作成については、こちらをご覧ください)Storeの実装例の型定義ファイルは、以下のこちらです。
1 2 3 4 5 6 7 8 9 10 | import { ComponentCustomProperties } from 'vue' import { Store } from 'vuex' import { State } from '@/store' declare module '@vue/runtime-core' { // this.$storeの型を宣言 interface ComponentCustomProperties { $store: Store<State> } } |
ComponentCustomProperties
の型を宣言します。
また、
$store
プロパティには以下のようにアクセスします。
1 2 3 4 5 | <template> <div> {{ $store.state.books }} </div> </template> |
さいごに
Vue 3がリリースされてから大分時間がたちましたが、ようやくVuexがComposition APIに対応してくれました。サードパーティのモジュールを使ってTypeScript対応していた方は、Vuex 4を試してみてはいかがでしょうか。