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を導入する場合は、以下の通りインストールします。
npm install vuex@next --save # or yarn add vuex@next --save
StoreをTypeScriptで実装する場合は、以下のように、main.ts
のcreateApp()
メソッドでstore
とkey
を渡します。
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をTypeScriptで実装した例がこちらです。
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
などを渡しています。
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
を生成します。
// InjectionKeyを定義する export const key: InjectionKey<Store<State>> = Symbol()
最後に注目する点は、独自のuseStoreメソッドを定義している箇所です。ComponentのSetup ()
メソッド内からStoreにアクセスするには、このuseStoreメソッドを使う必要があります。
加えて、型情報のついたStoreインスタンスを取得するためには、useStore()
メソッドに、先ほど生成したInjectionKeyを渡す必要があります。
上記のStoreの使用例はこちらです。
<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インスタンスを取得する必要があります。
// Storeを取得する const store = useStore()
Storeのパラメータをリアクティブにアクセスしたい場合は、computed()
メソッドを定義します。
また、Mutation
、もしくは、Action
にアクセスする場合は、methos
として定義します。
return { book, // v-model books: computed(() => store.state.books), // 本の一覧を返す add: () => store.dispatch('add', { name: book.name, author: book.author } as Book) // 本を追加する }
$store
プロパティに型をつけるには、型定義ファイルを作る必要があります(型定義ファイルの作成については、こちらをご覧ください)Storeの実装例の型定義ファイルは、以下のこちらです。
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
プロパティには以下のようにアクセスします。
<template> <div> {{ $store.state.books }} </div> </template>
Vue 3がリリースされてから大分時間がたちましたが、ようやくVuexがComposition APIに対応してくれました。サードパーティのモジュールを使ってTypeScript対応していた方は、Vuex 4を試してみてはいかがでしょうか。