はじめに
エンジニアとして働き始めて4ヶ月経ち5ヶ月目に突入したのですが、自分の技術力のなさに絶望しながら日々奮闘しています!
そんな僕が以前から気になっていたVuex+Typescript環境下でインテリセンスが効きにくいと感じていた問題を
解決してくれるvuex-module-decorators
というものを使用したのでメモとして残しておきます。
install
yarn add -D vuex-module-decorators
npm install -D vuex-module-decorators
Setup
~/utils/store-accessors.ts
ではinstallしたモジュールをインポートし、storeに登録します。
また、新しいstoreが出来次第このファイルに追加していきます。
// ~/utils/store-accessors.ts
import { Store } from "vuex";
import { getModule } from "vuex-module-decorators";
import User from "~/store/users";
// 新しいstoreが出来次第以下に追加していく
let usersStore: User;
// let exampleStore: Example
function initialiseStores(store: Store<any>): void {
usersStore = getModule(User, store);
}
export {
initialiseStores,
usersStore,
};
~/store/index.ts
ではコンポーネントからインポートできるようにstore-accessor.ts
のstoreをexportしています。
またstoreを初期化しています。
// ~/store/index.ts
import { Store } from 'vuex'
import { initialiseStores } from '~/utils/store-accessor'
const initializer = (store: Store<any>) => initialiseStores(store) //storeの初期化
export const plugins = [initializer]
export * from '~/utils/store-accessor' //storeをexportする
Storeの作成
こちらはstoreの全体イメージです。
MutationやActionなどは基本的にデコレーターを使用し作成します。
getterとstateはデコレーターを使用しないのは謎ですが、
そんなことはさておき、以下で順を追って解説をしていきます。
// ~/store/users.ts
import { Module, VuexModule, Mutation, Action } from "vuex-module-decorators";
import { $axios } from "~/plugins/api";
export type UserType = {
name: string;
age: number;
};
@Module({
name: "users",
stateFactory: true,
namespaced: true,
})
export default class User extends VuexModule {
private users: UserType[] = [];
public get getUsers() {
return this.users;
}
@Mutation
public addUsers(user: UserType) {
return this.users.push(user);
}
@Mutation
public setUsers(users: UserType[]) {
return this.users = users;
}
@Action
public async fetchUsersData() {
const { data } = await $axios.get<UserType[]>("/api/users/");
this.setUsers(data);
}
}
解説
必要なmoduleをインポートする
$axios
はAction
でapiを叩く際に使用するのでインポートしています。
import { Module, VuexModule, Mutation, Action } from "vuex-module-decorators";
import { $axios } from "~/plugins/api";
型定義
ここでは直接storeファイルに記述していますが、typesフォルダに切り分けて記述した方が綺麗かも。
export type UserType = {
name: string;
age: number;
};
Moduleを定義する
- nameにパスを指定。
-
stateFactory: true
とすることで、NuxtがモジュールモードでStoreを作ってくれます。 - namespced: trueとすることで、名前空間が適用されます
@Module({
name: "users",
stateFactory: true,
namespaced: true,
})
state作成
stateは基本的にクラス内からしかアクセスしないため、privateで定義します。
private users: UserType[] = [];
getter
usersの値を返すgetterです。
stateへはクラス内であればthis
でアクセスできます。
public get getUsers() {
return this.users;
}
Mutation
usersのデータをセットするMutationです。
ここでprivateで定義しているのは、ActionからのみMutationを実行するためです。
基本的にMutationはprivateで定義するのがベターかもしれません。
@Mutation
private setUsers(users: UserType[]) {
return this.users = users;
}
Action
以下のような非同期処理を実行する際にはAcitonを使用します。
取得したuserのdataを先ほどMutationで定義したsetUsers関数の引数に渡しています。
@Action
public async fetchUsersData() {
const { data } = await $axios.get<UserType[]>("/api/users/");
this.setUsers(data);
}
コンポーネント内でActionを実行する
import { usersStore } from "@/store";
const store = usersStore;
store.fetchUsersData(); //Actionを実行。
これでコンポーネント内でActionを実行することができました。
補完も効いてて最高です。
おわりに
以前からvuexを使用する際に補完が効かなかったりしてめんどくさいなと思っていたので
今後もVuexを使用する際はこちらを採用しようと思います。
もうcommitやdispatchとはおさらばなのかもしれませんね。